diff --git a/.changeset/free-windows-stand.md b/.changeset/free-windows-stand.md new file mode 100644 index 00000000..853d4a92 --- /dev/null +++ b/.changeset/free-windows-stand.md @@ -0,0 +1,7 @@ +--- +"@reshaped/headless": minor +"reshaped": minor +"@reshaped/utilities": minor +--- + +Extracted Reshaped provider and all of its dependencies into @reshaped/headless diff --git a/.storybook/preview.jsx b/.storybook/preview.jsx index 594bd2f8..f6ae304a 100644 --- a/.storybook/preview.jsx +++ b/.storybook/preview.jsx @@ -1,4 +1,5 @@ import React from "react"; +import { useRTL } from "../packages/headless"; import Reshaped from "../packages/reshaped/src/components/Reshaped"; import Button from "../packages/reshaped/src/components/Button"; import View from "../packages/reshaped/src/components/View"; @@ -6,9 +7,8 @@ import Text from "../packages/reshaped/src/components/Text"; import Hidden from "../packages/reshaped/src/components/Hidden"; import DropdownMenu from "../packages/reshaped/src/components/DropdownMenu"; import Icon from "../packages/reshaped/src/components/Icon"; -import useRTL from "../packages/reshaped/src/hooks/useRTL"; +import useTheme from "../packages/reshaped/src/components/Theme"; import IconCheckmark from "../packages/reshaped/src/icons/Checkmark"; -import { useTheme } from "../packages/reshaped/src/components/Theme"; import "../packages/reshaped/src/themes/reshaped/theme.css"; import "../packages/reshaped/src/themes/slate/theme.css"; import "../packages/reshaped/src/themes/figma/theme.css"; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts deleted file mode 100644 index 870dc6f1..00000000 --- a/packages/core/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -// Utilities -export { classNames, TrapFocus } from "@reshaped/utilities"; diff --git a/packages/core/CHANGELOG.md b/packages/headless/CHANGELOG.md similarity index 100% rename from packages/core/CHANGELOG.md rename to packages/headless/CHANGELOG.md diff --git a/packages/core/package.json b/packages/headless/package.json similarity index 78% rename from packages/core/package.json rename to packages/headless/package.json index 69a1f7c2..0b0eaf90 100644 --- a/packages/core/package.json +++ b/packages/headless/package.json @@ -27,12 +27,18 @@ "import": "./dist/index.js", "default": "./dist/index.js" }, + "./internal": { + "types": "./dist/internal.d.ts", + "import": "./dist/internal.js", + "default": "./dist/internal.js" + }, "./package.json": "./package.json" }, + "scripts": { - "clean": "rimraf dist", - "dev": "tsc --watch -p tsconfig.json", - "build": "pnpm clean && tsc -p tsconfig.json" + "clean": "rm -rf dist", + "dev": "tsc-watch --onSuccess \"resolve-tspaths\"", + "build": "tsc && resolve-tspaths" }, "peerDependencies": { "react": "^18 || ^19", diff --git a/packages/headless/src/components/Reshaped/Reshaped.tsx b/packages/headless/src/components/Reshaped/Reshaped.tsx new file mode 100644 index 00000000..396a20fc --- /dev/null +++ b/packages/headless/src/components/Reshaped/Reshaped.tsx @@ -0,0 +1,25 @@ +"use client"; + +import React from "react"; + +import { SingletonHotkeysProvider } from "@/hooks/_internal/useSingletonHotkeys"; +import { SingletonKeyboardModeProvider } from "@/hooks/_internal/useSingletonKeyboardMode"; +import { SingletonRTLProvider } from "@/hooks/_internal/useSingletonRTL"; + +import type * as T from "./Reshaped.types"; + +const Reshaped: React.FC = (props) => { + const { children } = props; + + return ( + + + {children} + + + ); +}; + +Reshaped.displayName = "Headless.ReshapedProvider"; + +export default Reshaped; diff --git a/packages/headless/src/components/Reshaped/Reshaped.types.ts b/packages/headless/src/components/Reshaped/Reshaped.types.ts new file mode 100644 index 00000000..34cf4d83 --- /dev/null +++ b/packages/headless/src/components/Reshaped/Reshaped.types.ts @@ -0,0 +1,6 @@ +import type React from "react"; + +export type Props = { + /** Node for inserting children */ + children?: React.ReactNode; +}; diff --git a/packages/headless/src/components/Reshaped/index.ts b/packages/headless/src/components/Reshaped/index.ts new file mode 100644 index 00000000..85d56de0 --- /dev/null +++ b/packages/headless/src/components/Reshaped/index.ts @@ -0,0 +1,2 @@ +export { default } from "./Reshaped"; +export type { Props as ReshapedProps } from "./Reshaped.types"; diff --git a/packages/reshaped/src/hooks/_private/useSingletonHotkeys.tsx b/packages/headless/src/hooks/_internal/useSingletonHotkeys.tsx similarity index 97% rename from packages/reshaped/src/hooks/_private/useSingletonHotkeys.tsx rename to packages/headless/src/hooks/_internal/useSingletonHotkeys.tsx index 36dfe53e..60644e63 100644 --- a/packages/reshaped/src/hooks/_private/useSingletonHotkeys.tsx +++ b/packages/headless/src/hooks/_internal/useSingletonHotkeys.tsx @@ -1,3 +1,5 @@ +"use client"; + import React from "react"; /** @@ -156,7 +158,7 @@ const globalHotkeyStore = new HotkeyStore(); /** * Components / Hooks */ -export const HotkeyContext = React.createContext({} as Context); +const HotkeyContext = React.createContext({} as Context); export const SingletonHotkeysProvider: React.FC<{ children: React.ReactNode }> = (props) => { const { children } = props; @@ -269,6 +271,4 @@ export const SingletonHotkeysProvider: React.FC<{ children: React.ReactNode }> = ); }; -const useSingletonHotkeys = () => React.useContext(HotkeyContext); - -export default useSingletonHotkeys; +export const useSingletonHotkeys = () => React.useContext(HotkeyContext); diff --git a/packages/reshaped/src/hooks/_private/useSingletonKeyboardMode.tsx b/packages/headless/src/hooks/_internal/useSingletonKeyboardMode.tsx similarity index 89% rename from packages/reshaped/src/hooks/_private/useSingletonKeyboardMode.tsx rename to packages/headless/src/hooks/_internal/useSingletonKeyboardMode.tsx index 6eadd8db..bb94f036 100644 --- a/packages/reshaped/src/hooks/_private/useSingletonKeyboardMode.tsx +++ b/packages/headless/src/hooks/_internal/useSingletonKeyboardMode.tsx @@ -3,7 +3,7 @@ import { activateKeyboardMode, deactivateKeyboardMode } from "@reshaped/utilities/internal"; import React from "react"; -import * as keys from "constants/keys"; +const ESC = "Escape"; type ContextProps = { disabledRef: React.RefObject | null; @@ -13,7 +13,7 @@ type ContextProps = { deactivate: () => void; }; -export const SingletonKeyboardModeContext = React.createContext({ +const SingletonKeyboardModeContext = React.createContext({ disabledRef: null, disable: () => {}, enable: () => {}, @@ -46,7 +46,7 @@ export const SingletonKeyboardModeProvider: React.FC<{ children: React.ReactNode (e: KeyboardEvent) => { if (e.metaKey || e.altKey || e.ctrlKey) return; // Prevent focus ring from appearing when using mouse but closing with esc - if (e.key === keys.ESC) return; + if (e.key === ESC) return; activate(); }, [activate] @@ -83,3 +83,5 @@ export const SingletonKeyboardModeProvider: React.FC<{ children: React.ReactNode ); }; + +export const useSingletonKeyboardMode = () => React.useContext(SingletonKeyboardModeContext); diff --git a/packages/headless/src/hooks/_internal/useSingletonRTL.tsx b/packages/headless/src/hooks/_internal/useSingletonRTL.tsx new file mode 100644 index 00000000..9fc2a7bd --- /dev/null +++ b/packages/headless/src/hooks/_internal/useSingletonRTL.tsx @@ -0,0 +1,59 @@ +"use client"; + +import { isRTL } from "@reshaped/utilities"; +import React from "react"; + +import useIsomorphicLayoutEffect from "@/hooks/useIsomorphicLayoutEffect"; + +type Context = { + rtl: [boolean, (state: boolean) => void]; +}; + +const SingletonRTLContext = React.createContext({ + rtl: [false, () => {}], +}); + +export const useSingletonRTL = (defaultRTL?: boolean) => { + const state = React.useState(defaultRTL || false); + const [rtl, setRTL] = state; + + /** + * Handle changing dir attribute directly + */ + useIsomorphicLayoutEffect(() => { + const observer = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + if (mutation.attributeName !== "dir") return; + + const nextRTL = isRTL(); + if (rtl !== nextRTL) setRTL(nextRTL); + }); + }); + + observer.observe(document.documentElement, { attributes: true }); + return () => observer.disconnect(); + }, [rtl]); + + /** + * Handle setRTL usage + */ + useIsomorphicLayoutEffect(() => { + document.documentElement.setAttribute("dir", rtl ? "rtl" : "ltr"); + }, [rtl]); + + return state; +}; + +export const SingletonRTLProvider: React.FC<{ + children: React.ReactNode; + defaultRTL?: boolean; +}> = (props) => { + const { children, defaultRTL } = props; + const rtlState = useSingletonRTL(defaultRTL); + + return ( + + {children} + + ); +}; diff --git a/packages/reshaped/src/hooks/useHotkeys.ts b/packages/headless/src/hooks/useHotkeys.ts similarity index 85% rename from packages/reshaped/src/hooks/useHotkeys.ts rename to packages/headless/src/hooks/useHotkeys.ts index d1f0e365..2334af40 100644 --- a/packages/reshaped/src/hooks/useHotkeys.ts +++ b/packages/headless/src/hooks/useHotkeys.ts @@ -2,14 +2,14 @@ import React from "react"; -import useSingletonHotkey, { type Hotkeys } from "./_private/useSingletonHotkeys"; +import { useSingletonHotkeys, type Hotkeys } from "@/hooks/_internal/useSingletonHotkeys"; const useHotkeys = ( hotkeys: Hotkeys, deps: unknown[] = [], options?: { ref?: React.RefObject; disabled?: boolean; preventDefault?: boolean } ) => { - const { addHotkeys, isPressed } = useSingletonHotkey(); + const { addHotkeys, isPressed } = useSingletonHotkeys(); const generatedRef = React.useRef(null); const elementRef = options?.ref || generatedRef; diff --git a/packages/reshaped/src/hooks/useIsomorphicLayoutEffect.ts b/packages/headless/src/hooks/useIsomorphicLayoutEffect.ts similarity index 100% rename from packages/reshaped/src/hooks/useIsomorphicLayoutEffect.ts rename to packages/headless/src/hooks/useIsomorphicLayoutEffect.ts diff --git a/packages/headless/src/hooks/useKeyboardMode.ts b/packages/headless/src/hooks/useKeyboardMode.ts new file mode 100644 index 00000000..681116d3 --- /dev/null +++ b/packages/headless/src/hooks/useKeyboardMode.ts @@ -0,0 +1,3 @@ +import { useSingletonKeyboardMode } from "@/hooks/_internal/useSingletonKeyboardMode"; + +export default useSingletonKeyboardMode; diff --git a/packages/headless/src/hooks/useRTL.ts b/packages/headless/src/hooks/useRTL.ts new file mode 100644 index 00000000..94853e24 --- /dev/null +++ b/packages/headless/src/hooks/useRTL.ts @@ -0,0 +1,3 @@ +import { useSingletonRTL } from "@/hooks/_internal/useSingletonRTL"; + +export default useSingletonRTL; diff --git a/packages/headless/src/index.ts b/packages/headless/src/index.ts new file mode 100644 index 00000000..67f2dcb4 --- /dev/null +++ b/packages/headless/src/index.ts @@ -0,0 +1,15 @@ +// Utilities +export { classNames, TrapFocus } from "@reshaped/utilities"; + +// Components +export { default as Reshaped, type ReshapedProps } from "./components/Reshaped"; + +// Hooks +export { default as useHotkeys } from "./hooks/useHotkeys"; +export { default as useKeyboardMode } from "./hooks/useKeyboardMode"; +export { default as useRTL } from "./hooks/useRTL"; +export { default as useIsomorphicLayoutEffect } from "./hooks/useIsomorphicLayoutEffect"; + +// Types +export type { ClassName } from "@reshaped/utilities"; +export type { Attributes, CSSVariable, StyleAttribute } from "@/types/global"; diff --git a/packages/headless/src/internal.ts b/packages/headless/src/internal.ts new file mode 100644 index 00000000..7ccd76a4 --- /dev/null +++ b/packages/headless/src/internal.ts @@ -0,0 +1,25 @@ +/** + * Internal utilities re-used in other Reshaped components but not meant to be used as a public API + * Their API is subject to change without a major version bump. + * + * If you want to use one of these utilities, open an issue or a PR about moving it to the public API file + */ + +export { lockScroll, Flyout } from "@reshaped/utilities"; + +export { + disableScroll, + enableScroll, + rafThrottle, + checkKeyboardMode, + findParent, + getFocusableElements, + focusableSelector, + focusFirstElement, + focusLastElement, + focusNextElement, + focusPreviousElement, + type FocusableElement, + type TrapMode, + type Coordinates, +} from "@reshaped/utilities/internal"; diff --git a/packages/headless/src/types/global.ts b/packages/headless/src/types/global.ts new file mode 100644 index 00000000..ef6c3126 --- /dev/null +++ b/packages/headless/src/types/global.ts @@ -0,0 +1,13 @@ +export type CSSVariable = `--${string}`; +export type StyleAttribute = + | React.CSSProperties + | (React.CSSProperties & Record); + +type DataAttributes = object | Record<`data-${string}`, string | boolean>; + +export type Attributes = + // If tag name is not passed, fallback to HTMLElement attributes + (TagName extends keyof React.JSX.IntrinsicElements + ? React.JSX.IntrinsicElements[TagName] + : React.HTMLAttributes) & + DataAttributes & { style?: StyleAttribute }; diff --git a/packages/core/tsconfig.json b/packages/headless/tsconfig.json similarity index 71% rename from packages/core/tsconfig.json rename to packages/headless/tsconfig.json index 40819885..e85fe220 100644 --- a/packages/core/tsconfig.json +++ b/packages/headless/tsconfig.json @@ -4,10 +4,11 @@ "outDir": "./dist", "baseUrl": "./src", "rootDir": "./src", + "target": "ES2022", "module": "ESNext", - "moduleResolution": "Node" + "moduleResolution": "Bundler", + "paths": { "@/*": ["./*"] } }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] } - diff --git a/packages/reshaped/package.json b/packages/reshaped/package.json index 4da247ba..3808254c 100644 --- a/packages/reshaped/package.json +++ b/packages/reshaped/package.json @@ -99,7 +99,7 @@ "react-dom": "^18 || ^19" }, "dependencies": { - "@reshaped/utilities": "workspace:*", + "@reshaped/headless": "workspace:*", "@csstools/postcss-global-data": "3.1.0", "chalk": "4.1.2", "commander": "14.0.2", diff --git a/packages/reshaped/src/components/Accordion/Accordion.types.ts b/packages/reshaped/src/components/Accordion/Accordion.types.ts index 93611d24..22ce34ca 100644 --- a/packages/reshaped/src/components/Accordion/Accordion.types.ts +++ b/packages/reshaped/src/components/Accordion/Accordion.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { IconProps } from "components/Icon"; import type React from "react"; import type * as G from "types/global"; @@ -14,9 +15,9 @@ export type BaseProps = { /** Callback when the accordion is expanded or collapsed */ onToggle?: (active: boolean) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type TriggerProps = { diff --git a/packages/reshaped/src/components/Accordion/AccordionControlled.tsx b/packages/reshaped/src/components/Accordion/AccordionControlled.tsx index e126c8c7..fc94a55f 100644 --- a/packages/reshaped/src/components/Accordion/AccordionControlled.tsx +++ b/packages/reshaped/src/components/Accordion/AccordionControlled.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import useElementId from "hooks/useElementId"; diff --git a/packages/reshaped/src/components/Accordion/AccordionTrigger.tsx b/packages/reshaped/src/components/Accordion/AccordionTrigger.tsx index da8a1eb9..576be592 100644 --- a/packages/reshaped/src/components/Accordion/AccordionTrigger.tsx +++ b/packages/reshaped/src/components/Accordion/AccordionTrigger.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import Actionable from "components/Actionable"; diff --git a/packages/reshaped/src/components/ActionBar/ActionBar.tsx b/packages/reshaped/src/components/ActionBar/ActionBar.tsx index 7b2fddd9..047470f9 100644 --- a/packages/reshaped/src/components/ActionBar/ActionBar.tsx +++ b/packages/reshaped/src/components/ActionBar/ActionBar.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import View from "components/View"; import { responsiveVariables } from "utilities/props"; diff --git a/packages/reshaped/src/components/ActionBar/ActionBar.types.ts b/packages/reshaped/src/components/ActionBar/ActionBar.types.ts index db36b6df..0475120d 100644 --- a/packages/reshaped/src/components/ActionBar/ActionBar.types.ts +++ b/packages/reshaped/src/components/ActionBar/ActionBar.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ViewProps } from "components/View"; import type React from "react"; import type * as G from "types/global"; @@ -18,7 +19,7 @@ export type Props = Pick; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Alert/Alert.types.ts b/packages/reshaped/src/components/Alert/Alert.types.ts index 7351e6c4..88baf72c 100644 --- a/packages/reshaped/src/components/Alert/Alert.types.ts +++ b/packages/reshaped/src/components/Alert/Alert.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { IconProps } from "components/Icon"; import type React from "react"; import type * as G from "types/global"; @@ -20,7 +21,7 @@ export type Props = { /** Apply negative margin and remove side borders, base unit token number multiplier */ bleed?: G.Responsive; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Autocomplete/Autocomplete.tsx b/packages/reshaped/src/components/Autocomplete/Autocomplete.tsx index db4fd731..59c7494a 100644 --- a/packages/reshaped/src/components/Autocomplete/Autocomplete.tsx +++ b/packages/reshaped/src/components/Autocomplete/Autocomplete.tsx @@ -1,5 +1,6 @@ "use client"; +import { useIsomorphicLayoutEffect, useHotkeys } from "@reshaped/headless"; import React from "react"; import DropdownMenu from "components/DropdownMenu"; @@ -7,8 +8,6 @@ import TextField from "components/TextField"; import * as keys from "constants/keys"; import useElementId from "hooks/useElementId"; import useHandlerRef from "hooks/useHandlerRef"; -import useHotkeys from "hooks/useHotkeys"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import s from "./Autocomplete.module.css"; import * as T from "./Autocomplete.types"; diff --git a/packages/reshaped/src/components/Avatar/Avatar.tsx b/packages/reshaped/src/components/Avatar/Avatar.tsx index d1f0b43c..3a38c2f7 100644 --- a/packages/reshaped/src/components/Avatar/Avatar.tsx +++ b/packages/reshaped/src/components/Avatar/Avatar.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import Icon from "components/Icon"; import Image, { type ImageProps } from "components/Image"; diff --git a/packages/reshaped/src/components/Avatar/Avatar.types.ts b/packages/reshaped/src/components/Avatar/Avatar.types.ts index 26d69d0c..f8d8f660 100644 --- a/packages/reshaped/src/components/Avatar/Avatar.types.ts +++ b/packages/reshaped/src/components/Avatar/Avatar.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { IconProps } from "components/Icon"; import type * as G from "types/global"; @@ -7,10 +8,10 @@ export type Props = { /** Image alt text */ alt?: string; /** Additional attributes for the image element */ - imageAttributes?: G.Attributes<"img">; + imageAttributes?: Attributes<"img">; /** Render prop for the image element, useful for integrating with the Image component from third party frameworks */ renderImage?: ( - attributes: Omit, "src" | "alt"> & { src: string; alt: string } + attributes: Omit, "src" | "alt"> & { src: string; alt: string } ) => React.ReactNode; /** Initials to display if no image is provided */ initials?: string; @@ -31,7 +32,7 @@ export type Props = { */ size?: G.Responsive; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Badge/Badge.tsx b/packages/reshaped/src/components/Badge/Badge.tsx index 0a047d50..ea1122a2 100644 --- a/packages/reshaped/src/components/Badge/Badge.tsx +++ b/packages/reshaped/src/components/Badge/Badge.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { forwardRef } from "react"; import Actionable, { type ActionableProps, type ActionableRef } from "components/Actionable"; diff --git a/packages/reshaped/src/components/Badge/Badge.types.ts b/packages/reshaped/src/components/Badge/Badge.types.ts index 9749d959..dfdc4ae7 100644 --- a/packages/reshaped/src/components/Badge/Badge.types.ts +++ b/packages/reshaped/src/components/Badge/Badge.types.ts @@ -1,7 +1,7 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ActionableProps } from "components/Actionable"; import type { IconProps } from "components/Icon"; import type React from "react"; -import type * as G from "types/global"; type BaseProps = { /** Component color scheme @@ -21,7 +21,7 @@ type BaseProps = { /** Transition the component to hidden state */ hidden?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; } & Pick; type WithChildren = BaseProps & { @@ -65,7 +65,7 @@ export type ContainerProps = { /** Node for inserting children to position the badge against */ children: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Badge/BadgeContainer.tsx b/packages/reshaped/src/components/Badge/BadgeContainer.tsx index 87493b88..593710b7 100644 --- a/packages/reshaped/src/components/Badge/BadgeContainer.tsx +++ b/packages/reshaped/src/components/Badge/BadgeContainer.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import s from "./Badge.module.css"; diff --git a/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.tsx b/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.tsx index 7747a7f0..7930c2fa 100644 --- a/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.tsx +++ b/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import Button from "components/Button"; diff --git a/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.types.ts b/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.types.ts index 8794cecc..0d011105 100644 --- a/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.types.ts +++ b/packages/reshaped/src/components/Breadcrumbs/Breadcrumbs.types.ts @@ -1,6 +1,6 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { LinkProps } from "components/Link"; import type React from "react"; -import type * as G from "types/global"; export type Props = { /** Node for inserting children to position items */ @@ -21,9 +21,9 @@ export type Props = { /** aria-label attribute for the component */ ariaLabel?: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"nav">; + attributes?: Attributes<"nav">; }; export type ItemProps = Pick; diff --git a/packages/reshaped/src/components/Button/Button.tsx b/packages/reshaped/src/components/Button/Button.tsx index d0877176..2285a740 100644 --- a/packages/reshaped/src/components/Button/Button.tsx +++ b/packages/reshaped/src/components/Button/Button.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { forwardRef } from "react"; import Actionable, { type ActionableRef } from "components/Actionable"; diff --git a/packages/reshaped/src/components/Button/Button.types.ts b/packages/reshaped/src/components/Button/Button.types.ts index d10e7669..1498ee84 100644 --- a/packages/reshaped/src/components/Button/Button.types.ts +++ b/packages/reshaped/src/components/Button/Button.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { AlignerProps as BaseAlignerProps } from "components/_private/Aligner"; import type { ActionableProps } from "components/Actionable"; import type { IconProps } from "components/Icon"; @@ -53,9 +54,9 @@ export type GroupProps = { /** Node for inserting child Button components */ children: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type AlignerProps = BaseAlignerProps & { diff --git a/packages/reshaped/src/components/Button/ButtonGroup.tsx b/packages/reshaped/src/components/Button/ButtonGroup.tsx index 8714bac6..c3ec0a41 100644 --- a/packages/reshaped/src/components/Button/ButtonGroup.tsx +++ b/packages/reshaped/src/components/Button/ButtonGroup.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import s from "./Button.module.css"; diff --git a/packages/reshaped/src/components/Calendar/CalendarDate.tsx b/packages/reshaped/src/components/Calendar/CalendarDate.tsx index 09cc2226..a2729a52 100644 --- a/packages/reshaped/src/components/Calendar/CalendarDate.tsx +++ b/packages/reshaped/src/components/Calendar/CalendarDate.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import Actionable from "components/Actionable"; diff --git a/packages/reshaped/src/components/Calendar/useCalendarKeyboardNavigation.ts b/packages/reshaped/src/components/Calendar/useCalendarKeyboardNavigation.ts index 842aaa61..48a973ba 100644 --- a/packages/reshaped/src/components/Calendar/useCalendarKeyboardNavigation.ts +++ b/packages/reshaped/src/components/Calendar/useCalendarKeyboardNavigation.ts @@ -1,7 +1,7 @@ +import { useHotkeys } from "@reshaped/headless"; import React from "react"; import * as keys from "constants/keys"; -import useHotkeys from "hooks/useHotkeys"; import { getFocusableDates } from "./Calendar.utils"; diff --git a/packages/reshaped/src/components/Card/Card.tsx b/packages/reshaped/src/components/Card/Card.tsx index 3710cbaf..6a9e6dae 100644 --- a/packages/reshaped/src/components/Card/Card.tsx +++ b/packages/reshaped/src/components/Card/Card.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React, { forwardRef } from "react"; import Actionable from "components/Actionable"; diff --git a/packages/reshaped/src/components/Card/Card.types.ts b/packages/reshaped/src/components/Card/Card.types.ts index 1591cd88..4107cb10 100644 --- a/packages/reshaped/src/components/Card/Card.types.ts +++ b/packages/reshaped/src/components/Card/Card.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ActionableProps } from "components/Actionable"; import type { ViewProps } from "components/View"; import type React from "react"; @@ -20,9 +21,9 @@ export type Props & ActionableProps["attributes"]; + attributes?: Attributes & ActionableProps["attributes"]; /** Custom component tag name * @default "div" */ diff --git a/packages/reshaped/src/components/Carousel/Carousel.tsx b/packages/reshaped/src/components/Carousel/Carousel.tsx index 2e2c3efb..906b2b38 100644 --- a/packages/reshaped/src/components/Carousel/Carousel.tsx +++ b/packages/reshaped/src/components/Carousel/Carousel.tsx @@ -1,12 +1,10 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { rafThrottle } from "@reshaped/utilities/internal"; +import { classNames, useIsomorphicLayoutEffect, useRTL } from "@reshaped/headless"; +import { rafThrottle } from "@reshaped/headless/internal"; import React from "react"; import View from "components/View"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; -import useRTL from "hooks/useRTL"; import { responsiveVariables, responsiveClassNames } from "utilities/props"; import s from "./Carousel.module.css"; diff --git a/packages/reshaped/src/components/Carousel/Carousel.types.ts b/packages/reshaped/src/components/Carousel/Carousel.types.ts index 5b97ea0f..08368530 100644 --- a/packages/reshaped/src/components/Carousel/Carousel.types.ts +++ b/packages/reshaped/src/components/Carousel/Carousel.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ActionableRef } from "components/Actionable"; import type React from "react"; import type * as G from "types/global"; @@ -41,7 +42,7 @@ export type Props = { /** Callback when the carousel scrolls */ onScroll?: (e: React.UIEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Carousel/CarouselControl.tsx b/packages/reshaped/src/components/Carousel/CarouselControl.tsx index d4c75336..c7520e6d 100644 --- a/packages/reshaped/src/components/Carousel/CarouselControl.tsx +++ b/packages/reshaped/src/components/Carousel/CarouselControl.tsx @@ -1,10 +1,9 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; import { forwardRef, useState } from "react"; import Button from "components/Button"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import IconChevronLeft from "icons/ChevronLeft"; import IconChevronRight from "icons/ChevronRight"; diff --git a/packages/reshaped/src/components/Checkbox/Checkbox.tsx b/packages/reshaped/src/components/Checkbox/Checkbox.tsx index 45e394e0..45e777d1 100644 --- a/packages/reshaped/src/components/Checkbox/Checkbox.tsx +++ b/packages/reshaped/src/components/Checkbox/Checkbox.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; import { useCheckboxGroup } from "components/CheckboxGroup"; @@ -8,7 +8,6 @@ import { useFormControl } from "components/FormControl"; import HiddenInput from "components/HiddenInput"; import Icon from "components/Icon"; import Text from "components/Text"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import IconCheckmark from "icons/Checkmark"; import { responsiveClassNames, responsivePropDependency } from "utilities/props"; diff --git a/packages/reshaped/src/components/Checkbox/Checkbox.types.ts b/packages/reshaped/src/components/Checkbox/Checkbox.types.ts index af3da6b2..67ff56ad 100644 --- a/packages/reshaped/src/components/Checkbox/Checkbox.types.ts +++ b/packages/reshaped/src/components/Checkbox/Checkbox.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -25,11 +26,11 @@ type BaseProps = { /** Callback when the component is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"label">; + attributes?: Attributes<"label">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; }; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/Container/Container.tsx b/packages/reshaped/src/components/Container/Container.tsx index 177b9c06..09415d79 100644 --- a/packages/reshaped/src/components/Container/Container.tsx +++ b/packages/reshaped/src/components/Container/Container.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Container/Container.types.ts b/packages/reshaped/src/components/Container/Container.types.ts index dcb48cea..6df4092c 100644 --- a/packages/reshaped/src/components/Container/Container.types.ts +++ b/packages/reshaped/src/components/Container/Container.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ViewProps } from "components/View"; import type React from "react"; import type * as G from "types/global"; @@ -14,7 +15,7 @@ export type Props = Pick; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/ContextMenu/ContextMenu.tsx b/packages/reshaped/src/components/ContextMenu/ContextMenu.tsx index c35faf23..04fb8091 100644 --- a/packages/reshaped/src/components/ContextMenu/ContextMenu.tsx +++ b/packages/reshaped/src/components/ContextMenu/ContextMenu.tsx @@ -9,11 +9,11 @@ import useScrollLock from "hooks/useScrollLock"; import s from "./ContextMenu.module.css"; import type * as T from "./ContextMenu.types"; -import type * as G from "types/global"; +import type { Coordinates } from "@reshaped/headless/internal"; const ContextMenu: React.FC = (props) => { const { position = "end-top", onOpen, onClose, ...dropdownMenuProps } = props; - const [coordinates, setCoordinates] = React.useState(); + const [coordinates, setCoordinates] = React.useState(); const originRef = React.useRef(null); const { lockScroll, unlockScroll } = useScrollLock({ originRef }); const onOpenRef = useHandlerRef(onOpen); diff --git a/packages/reshaped/src/components/Dismissible/Dismissible.tsx b/packages/reshaped/src/components/Dismissible/Dismissible.tsx index 14d99e75..4eeab6a7 100644 --- a/packages/reshaped/src/components/Dismissible/Dismissible.tsx +++ b/packages/reshaped/src/components/Dismissible/Dismissible.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import Button from "components/Button"; import IconClose from "icons/Close"; diff --git a/packages/reshaped/src/components/Dismissible/Dismissible.types.ts b/packages/reshaped/src/components/Dismissible/Dismissible.types.ts index 02afef33..9d297904 100644 --- a/packages/reshaped/src/components/Dismissible/Dismissible.types.ts +++ b/packages/reshaped/src/components/Dismissible/Dismissible.types.ts @@ -1,5 +1,5 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; -import type * as G from "types/global"; type WithClose = { /** Hide the close button */ @@ -25,7 +25,7 @@ export type Props = CloseProps & { /** Callback when the component is dismissed */ onClose?: () => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Divider/Divider.tsx b/packages/reshaped/src/components/Divider/Divider.tsx index 01635ce0..a8861577 100644 --- a/packages/reshaped/src/components/Divider/Divider.tsx +++ b/packages/reshaped/src/components/Divider/Divider.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames, type StyleAttribute } from "@reshaped/headless"; import Text from "components/Text"; import { responsiveClassNames } from "utilities/props"; @@ -7,7 +7,6 @@ import s from "./Divider.module.css"; import type * as T from "./Divider.types"; import type React from "react"; -import type * as G from "types/global"; const Divider: React.FC = (props) => { const { @@ -41,7 +40,7 @@ const Divider: React.FC = (props) => { role="separator" aria-orientation={ariaOrientation} className={rootClassNames} - style={{ ...attributes?.style, "--rs-divider-offset": offset } as G.StyleAttribute} + style={{ ...attributes?.style, "--rs-divider-offset": offset } as StyleAttribute} > {children && ( diff --git a/packages/reshaped/src/components/Divider/Divider.types.ts b/packages/reshaped/src/components/Divider/Divider.types.ts index 95bcd796..696e933e 100644 --- a/packages/reshaped/src/components/Divider/Divider.types.ts +++ b/packages/reshaped/src/components/Divider/Divider.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -17,7 +18,7 @@ export type Props = { /** Node for inserting text labels or custom components as a part of divider */ children?: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"hr">; + attributes?: Attributes<"hr">; }; diff --git a/packages/reshaped/src/components/DropdownMenu/DropdownMenu.tsx b/packages/reshaped/src/components/DropdownMenu/DropdownMenu.tsx index 91467faa..7ff1157b 100644 --- a/packages/reshaped/src/components/DropdownMenu/DropdownMenu.tsx +++ b/packages/reshaped/src/components/DropdownMenu/DropdownMenu.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useHotkeys, useRTL } from "@reshaped/headless"; import React from "react"; import { useFlyoutContext } from "components/Flyout"; @@ -8,8 +8,6 @@ import Icon from "components/Icon"; import MenuItem from "components/MenuItem"; import Popover from "components/Popover"; import * as keys from "constants/keys"; -import useHotkeys from "hooks/useHotkeys"; -import useRTL from "hooks/useRTL"; import IconChevronRight from "icons/ChevronRight"; import s from "./DropdownMenu.module.css"; diff --git a/packages/reshaped/src/components/FileUpload/FileUpload.tsx b/packages/reshaped/src/components/FileUpload/FileUpload.tsx index a4992a8c..085515a7 100644 --- a/packages/reshaped/src/components/FileUpload/FileUpload.tsx +++ b/packages/reshaped/src/components/FileUpload/FileUpload.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import HiddenVisually from "components/HiddenVisually"; diff --git a/packages/reshaped/src/components/FileUpload/FileUpload.types.ts b/packages/reshaped/src/components/FileUpload/FileUpload.types.ts index 8f36032d..d032551e 100644 --- a/packages/reshaped/src/components/FileUpload/FileUpload.types.ts +++ b/packages/reshaped/src/components/FileUpload/FileUpload.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ViewProps } from "components/View"; import type React from "react"; import type * as G from "types/global"; @@ -21,11 +22,11 @@ export type Props = { /** Change component to render inline making it more compact */ inline?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; }; export type TriggerProps = { diff --git a/packages/reshaped/src/components/Flyout/Flyout.types.ts b/packages/reshaped/src/components/Flyout/Flyout.types.ts index 77cbaaa6..1e68088b 100644 --- a/packages/reshaped/src/components/Flyout/Flyout.types.ts +++ b/packages/reshaped/src/components/Flyout/Flyout.types.ts @@ -1,7 +1,7 @@ import React from "react"; -import type { TrapMode } from "@reshaped/utilities/internal"; -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; +import type { TrapMode, Coordinates } from "@reshaped/headless/internal"; /** * Utility @@ -135,7 +135,7 @@ type BaseProps = { */ autoFocus?: boolean; /** Origin coordinates for the content when there is no trigger element */ - originCoordinates?: G.Coordinates; + originCoordinates?: Coordinates; /** Node for inserting children */ children?: React.ReactNode; /** Callback when the content is opened */ @@ -157,7 +157,7 @@ type BaseProps = { /** Additional classname for the content element */ contentClassName?: string; /** Additional attributes for the content element */ - contentAttributes?: G.Attributes<"div">; + contentAttributes?: Attributes<"div">; /** Ref accessor for the flyout methods */ instanceRef?: React.Ref; /** Container to render the content in using a portal, position is calculated based on the container bounds @@ -188,9 +188,9 @@ export type ContentProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the content element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the content element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ContextProps = { diff --git a/packages/reshaped/src/components/Flyout/FlyoutContent.tsx b/packages/reshaped/src/components/Flyout/FlyoutContent.tsx index 9ac860d5..41d5fb10 100644 --- a/packages/reshaped/src/components/Flyout/FlyoutContent.tsx +++ b/packages/reshaped/src/components/Flyout/FlyoutContent.tsx @@ -1,10 +1,9 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; import Portal from "components/_private/Portal"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import { useFlyoutContext, ContentProvider } from "./Flyout.context"; import s from "./Flyout.module.css"; diff --git a/packages/reshaped/src/components/Flyout/FlyoutControlled.tsx b/packages/reshaped/src/components/Flyout/FlyoutControlled.tsx index 1c0ac91f..b47ca420 100644 --- a/packages/reshaped/src/components/Flyout/FlyoutControlled.tsx +++ b/packages/reshaped/src/components/Flyout/FlyoutControlled.tsx @@ -1,15 +1,17 @@ "use client"; -import { TrapFocus } from "@reshaped/utilities"; -import { checkKeyboardMode, type FocusableElement } from "@reshaped/utilities/internal"; +import { TrapFocus, useHotkeys, useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { + checkKeyboardMode, + type FocusableElement, + type Coordinates, +} from "@reshaped/headless/internal"; import React from "react"; import useIsDismissible from "hooks/_private/useIsDismissible"; import usePrevious from "hooks/_private/usePrevious"; import useElementId from "hooks/useElementId"; import useHandlerRef from "hooks/useHandlerRef"; -import useHotkeys from "hooks/useHotkeys"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import useOnClickOutside from "hooks/useOnClickOutside"; import { checkTransitions } from "utilities/animation"; @@ -25,7 +27,6 @@ import cooldown from "./utilities/cooldown"; import { createSafeArea } from "./utilities/safeArea"; import type * as T from "./Flyout.types"; -import type * as G from "types/global"; const FlyoutControlled: React.FC = (props) => { const { @@ -101,9 +102,9 @@ const FlyoutControlled: React.FC = (props) = // So we're saving a flag on touch start and then change the mouse enter behavior const hoverTriggeredWithTouchEventRef = React.useRef(false); // Cleanup function for safe area tracking - const safeAreaRef = React.useRef<{ origin: G.Coordinates; cleanup: () => void } | null>(null); + const safeAreaRef = React.useRef<{ origin: Coordinates; cleanup: () => void } | null>(null); - const originCoordinatesRef = React.useRef(originCoordinates ?? null); + const originCoordinatesRef = React.useRef(originCoordinates ?? null); originCoordinatesRef.current = originCoordinates ?? null; const flyout = useFlyout({ diff --git a/packages/reshaped/src/components/Flyout/useFlyout.ts b/packages/reshaped/src/components/Flyout/useFlyout.ts index 263b83f4..083e7fea 100644 --- a/packages/reshaped/src/components/Flyout/useFlyout.ts +++ b/packages/reshaped/src/components/Flyout/useFlyout.ts @@ -1,10 +1,8 @@ -import { Flyout } from "@reshaped/utilities"; +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { Flyout, type Coordinates } from "@reshaped/headless/internal"; import { useCallback, useMemo, useRef, useState } from "react"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; - import type * as T from "./Flyout.types"; -import type * as G from "types/global"; type UseFlyout = ( args: Pick< @@ -22,7 +20,7 @@ type UseFlyout = ( container?: HTMLElement | null; triggerElRef: React.RefObject; flyoutElRef: React.RefObject; - triggerCoordinatesRef: React.RefObject; + triggerCoordinatesRef: React.RefObject; } ) => Pick & { updatePosition: (options?: { fallback?: boolean }) => void; diff --git a/packages/reshaped/src/components/Flyout/utilities/safeArea.ts b/packages/reshaped/src/components/Flyout/utilities/safeArea.ts index 2e843ff1..c38a9ff6 100644 --- a/packages/reshaped/src/components/Flyout/utilities/safeArea.ts +++ b/packages/reshaped/src/components/Flyout/utilities/safeArea.ts @@ -1,19 +1,19 @@ -import type * as G from "types/global"; +import type { Coordinates } from "@reshaped/headless/internal"; type SafePolygonOptions = { contentRef: React.RefObject; triggerRef: React.RefObject; position: string | null | undefined; onClose: () => void; - origin: G.Coordinates; + origin: Coordinates; }; /** * Checks if a point is inside a triangle using barycentric coordinates */ function isPointInTriangle( - point: G.Coordinates, - triangle: [G.Coordinates, G.Coordinates, G.Coordinates] + point: Coordinates, + triangle: [Coordinates, Coordinates, Coordinates] ): boolean { const [p1, p2, p3] = triangle; @@ -32,7 +32,7 @@ function isPointInTriangle( function getContentCorners( contentRect: DOMRect, position: string | null | undefined -): [G.Coordinates, G.Coordinates] { +): [Coordinates, Coordinates] { const corners = { topLeft: { x: contentRect.left, y: contentRect.top }, topRight: { x: contentRect.right, y: contentRect.top }, @@ -55,7 +55,7 @@ function getContentCorners( * Checks if the mouse is over the trigger or content elements */ function isMouseOverElement( - point: G.Coordinates, + point: Coordinates, contentRef: React.RefObject, triggerRef: React.RefObject ): boolean { @@ -94,7 +94,7 @@ export function createSafeArea(options: SafePolygonOptions): () => void { origin.x -= buffer; } - const triangle: [G.Coordinates, G.Coordinates, G.Coordinates] = [origin, corner1, corner2]; + const triangle: [Coordinates, Coordinates, Coordinates] = [origin, corner1, corner2]; let timeoutId: ReturnType | null = null; @@ -115,7 +115,7 @@ export function createSafeArea(options: SafePolygonOptions): () => void { }; const handleMouseMove = (e: MouseEvent) => { - const currentPoint: G.Coordinates = { x: e.clientX, y: e.clientY }; + const currentPoint: Coordinates = { x: e.clientX, y: e.clientY }; if (isMouseOverElement(currentPoint, contentRef, triggerRef)) { cleanup(); diff --git a/packages/reshaped/src/components/Grid/Grid.tsx b/packages/reshaped/src/components/Grid/Grid.tsx index be176589..5a48255f 100644 --- a/packages/reshaped/src/components/Grid/Grid.tsx +++ b/packages/reshaped/src/components/Grid/Grid.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { resolveMixin } from "styles/mixin"; import { responsiveVariables, responsivePropDependency } from "utilities/props"; diff --git a/packages/reshaped/src/components/Grid/Grid.types.ts b/packages/reshaped/src/components/Grid/Grid.types.ts index 825cfad4..df0a22aa 100644 --- a/packages/reshaped/src/components/Grid/Grid.types.ts +++ b/packages/reshaped/src/components/Grid/Grid.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { Property } from "csstype"; import type React from "react"; import type * as TStyles from "styles/types"; @@ -39,9 +40,9 @@ export type Props; + attributes?: Attributes; }; export type ItemProps = { @@ -66,7 +67,7 @@ export type ItemProps; + attributes?: Attributes; }; diff --git a/packages/reshaped/src/components/Hidden/Hidden.tsx b/packages/reshaped/src/components/Hidden/Hidden.tsx index c17abb03..9a5bd4e5 100644 --- a/packages/reshaped/src/components/Hidden/Hidden.tsx +++ b/packages/reshaped/src/components/Hidden/Hidden.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { responsiveClassNames } from "utilities/props"; diff --git a/packages/reshaped/src/components/HiddenInput/HiddenInput.tsx b/packages/reshaped/src/components/HiddenInput/HiddenInput.tsx index 141b095b..2b2029b8 100644 --- a/packages/reshaped/src/components/HiddenInput/HiddenInput.tsx +++ b/packages/reshaped/src/components/HiddenInput/HiddenInput.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { useCheckboxGroup } from "components/CheckboxGroup"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/HiddenInput/HiddenInput.types.ts b/packages/reshaped/src/components/HiddenInput/HiddenInput.types.ts index 8b3228a5..929814ce 100644 --- a/packages/reshaped/src/components/HiddenInput/HiddenInput.types.ts +++ b/packages/reshaped/src/components/HiddenInput/HiddenInput.types.ts @@ -1,5 +1,6 @@ import React from "react"; +import type { Attributes, ClassName } from "@reshaped/headless"; import type * as G from "types/global"; export type Props = { @@ -22,7 +23,7 @@ export type Props = { /** Type of the input element */ type: "checkbox" | "radio"; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the input element */ - attributes?: G.Attributes<"input">; + attributes?: Attributes<"input">; }; diff --git a/packages/reshaped/src/components/Hotkey/Hotkey.tsx b/packages/reshaped/src/components/Hotkey/Hotkey.tsx index 5f97af14..f087e963 100644 --- a/packages/reshaped/src/components/Hotkey/Hotkey.tsx +++ b/packages/reshaped/src/components/Hotkey/Hotkey.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import Text from "components/Text"; diff --git a/packages/reshaped/src/components/Hotkey/Hotkey.types.ts b/packages/reshaped/src/components/Hotkey/Hotkey.types.ts index 2a11e7cf..06789fd7 100644 --- a/packages/reshaped/src/components/Hotkey/Hotkey.types.ts +++ b/packages/reshaped/src/components/Hotkey/Hotkey.types.ts @@ -1,5 +1,5 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; -import type * as G from "types/global"; export type Props = { /** Node for inserting children */ @@ -7,7 +7,7 @@ export type Props = { /** Highlight the component, can be used to show when hotkey is pressed */ active?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"span">; + attributes?: Attributes<"span">; }; diff --git a/packages/reshaped/src/components/Hotkey/tests/Hotkey.stories.tsx b/packages/reshaped/src/components/Hotkey/tests/Hotkey.stories.tsx index ff24b90c..0cbc1dd4 100644 --- a/packages/reshaped/src/components/Hotkey/tests/Hotkey.stories.tsx +++ b/packages/reshaped/src/components/Hotkey/tests/Hotkey.stories.tsx @@ -1,9 +1,9 @@ +import { useHotkeys } from "@reshaped/headless"; import { StoryObj } from "@storybook/react-vite"; import { expect } from "storybook/test"; import TextField from "components/TextField"; import View from "components/View"; -import useHotkeys from "hooks/useHotkeys"; import { Example } from "utilities/storybook"; import Hotkey from "../Hotkey"; diff --git a/packages/reshaped/src/components/Icon/Icon.tsx b/packages/reshaped/src/components/Icon/Icon.tsx index 1b974e51..92c52000 100644 --- a/packages/reshaped/src/components/Icon/Icon.tsx +++ b/packages/reshaped/src/components/Icon/Icon.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { resolveMixin } from "styles/mixin"; diff --git a/packages/reshaped/src/components/Icon/Icon.types.ts b/packages/reshaped/src/components/Icon/Icon.types.ts index f7e411f7..4947a055 100644 --- a/packages/reshaped/src/components/Icon/Icon.types.ts +++ b/packages/reshaped/src/components/Icon/Icon.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -18,7 +19,7 @@ export type Props = { /** Use the width of the svg asset instead of providing a square bounding box */ autoWidth?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"span">; + attributes?: Attributes<"span">; }; diff --git a/packages/reshaped/src/components/Image/Image.tsx b/packages/reshaped/src/components/Image/Image.tsx index 49b40a21..c85db4c3 100644 --- a/packages/reshaped/src/components/Image/Image.tsx +++ b/packages/reshaped/src/components/Image/Image.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { resolveMixin } from "styles/mixin"; diff --git a/packages/reshaped/src/components/Image/Image.types.ts b/packages/reshaped/src/components/Image/Image.types.ts index cf3467f6..524b02fa 100644 --- a/packages/reshaped/src/components/Image/Image.types.ts +++ b/packages/reshaped/src/components/Image/Image.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as TStyles from "styles/types"; import type * as G from "types/global"; @@ -30,12 +31,12 @@ export type Props = { /** Image render function, can be used for integrating with Image component in 3rd party frameworks */ renderImage?: ( // Next.js requires you to pass src and alt - attributes: Omit, "src" | "alt"> & { src: string; alt: string } + attributes: Omit, "src" | "alt"> & { src: string; alt: string } ) => React.ReactNode; /** Additional attributes for the image element */ - imageAttributes?: G.Attributes<"img">; + imageAttributes?: Attributes<"img">; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div"> & G.Attributes<"img">; + attributes?: Attributes<"div"> & Attributes<"img">; }; diff --git a/packages/reshaped/src/components/Link/Link.tsx b/packages/reshaped/src/components/Link/Link.tsx index 0304628d..157e6fa2 100644 --- a/packages/reshaped/src/components/Link/Link.tsx +++ b/packages/reshaped/src/components/Link/Link.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { forwardRef } from "react"; import Actionable, { type ActionableRef } from "components/Actionable"; diff --git a/packages/reshaped/src/components/Loader/Loader.tsx b/packages/reshaped/src/components/Loader/Loader.tsx index ca8bc07b..d0d063e9 100644 --- a/packages/reshaped/src/components/Loader/Loader.tsx +++ b/packages/reshaped/src/components/Loader/Loader.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { responsiveClassNames } from "utilities/props"; diff --git a/packages/reshaped/src/components/Loader/Loader.types.ts b/packages/reshaped/src/components/Loader/Loader.types.ts index eaf8ac90..23a0dc4f 100644 --- a/packages/reshaped/src/components/Loader/Loader.types.ts +++ b/packages/reshaped/src/components/Loader/Loader.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type * as G from "types/global"; export type Props = { @@ -8,7 +9,7 @@ export type Props = { /** aria-label attribute for the root element */ ariaLabel?: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"span">; + attributes?: Attributes<"span">; }; diff --git a/packages/reshaped/src/components/MenuItem/MenuItem.tsx b/packages/reshaped/src/components/MenuItem/MenuItem.tsx index 54751f3a..8355a362 100644 --- a/packages/reshaped/src/components/MenuItem/MenuItem.tsx +++ b/packages/reshaped/src/components/MenuItem/MenuItem.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { forwardRef } from "react"; import Actionable, { type ActionableRef } from "components/Actionable"; diff --git a/packages/reshaped/src/components/MenuItem/MenuItem.types.ts b/packages/reshaped/src/components/MenuItem/MenuItem.types.ts index 9f1db17f..f7e1ca9f 100644 --- a/packages/reshaped/src/components/MenuItem/MenuItem.types.ts +++ b/packages/reshaped/src/components/MenuItem/MenuItem.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ActionableProps } from "components/Actionable"; import type { IconProps } from "components/Icon"; import type React from "react"; @@ -41,7 +42,7 @@ export type AlignerProps = { /** Node for inserting children */ children: React.ReactElement; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Modal/Modal.tsx b/packages/reshaped/src/components/Modal/Modal.tsx index 70e9a866..5314a9a8 100644 --- a/packages/reshaped/src/components/Modal/Modal.tsx +++ b/packages/reshaped/src/components/Modal/Modal.tsx @@ -1,7 +1,7 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { enableScroll, disableScroll } from "@reshaped/utilities/internal"; +import { classNames } from "@reshaped/headless"; +import { enableScroll, disableScroll } from "@reshaped/headless/internal"; import React from "react"; import Overlay from "components/Overlay"; diff --git a/packages/reshaped/src/components/Modal/Modal.types.ts b/packages/reshaped/src/components/Modal/Modal.types.ts index bce6089c..0fa30c54 100644 --- a/packages/reshaped/src/components/Modal/Modal.types.ts +++ b/packages/reshaped/src/components/Modal/Modal.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { OverlayProps, OverlayCloseReason } from "components/Overlay"; import type React from "react"; import type * as G from "types/global"; @@ -48,11 +49,11 @@ export type Props = { /** aria-label attribute for the root element, useful when there is no visible title */ ariaLabel?: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional classname for the overlay element */ - overlayClassName?: G.ClassName; + overlayClassName?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div"> & { ref?: React.RefObject }; + attributes?: Attributes<"div"> & { ref?: React.RefObject }; } & Pick< OverlayProps, "onOpen" | "onAfterOpen" | "onAfterClose" | "active" | "containerRef" | "contained" diff --git a/packages/reshaped/src/components/NumberField/NumberFieldControlled.tsx b/packages/reshaped/src/components/NumberField/NumberFieldControlled.tsx index ffe81cd4..af3f44de 100644 --- a/packages/reshaped/src/components/NumberField/NumberFieldControlled.tsx +++ b/packages/reshaped/src/components/NumberField/NumberFieldControlled.tsx @@ -1,5 +1,6 @@ "use client"; +import { useHotkeys } from "@reshaped/headless"; import React from "react"; import Actionable from "components/Actionable"; @@ -9,7 +10,6 @@ import TextField, { TextFieldProps } from "components/TextField"; import * as keys from "constants/keys"; import useElementId from "hooks/useElementId"; import useHandlerRef from "hooks/useHandlerRef"; -import useHotkeys from "hooks/useHotkeys"; import IconChevronDown from "icons/ChevronDown"; import IconChevronUp from "icons/ChevronUp"; import IconMinus from "icons/Minus"; diff --git a/packages/reshaped/src/components/Overlay/Overlay.tsx b/packages/reshaped/src/components/Overlay/Overlay.tsx index 293d0e55..ad14e453 100644 --- a/packages/reshaped/src/components/Overlay/Overlay.tsx +++ b/packages/reshaped/src/components/Overlay/Overlay.tsx @@ -1,15 +1,13 @@ "use client"; -import { TrapFocus } from "@reshaped/utilities"; -import { classNames } from "@reshaped/utilities"; -import { type FocusableElement } from "@reshaped/utilities/internal"; +import { TrapFocus, useHotkeys, useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { classNames } from "@reshaped/headless"; +import { type FocusableElement } from "@reshaped/headless/internal"; import React from "react"; import Portal from "components/_private/Portal"; import useIsDismissible from "hooks/_private/useIsDismissible"; import useHandlerRef from "hooks/useHandlerRef"; -import useHotkeys from "hooks/useHotkeys"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import useScrollLock from "hooks/useScrollLock"; import useToggle from "hooks/useToggle"; import { onNextFrame } from "utilities/animation"; @@ -151,9 +149,9 @@ const Overlay: React.FC = (props) => { if (containerEl) { originalOverflowRef.current = containerEl.style.overflow; - // eslint-disable-next-line react-hooks/immutability - containerEl.style.overflow = "hidden"; - containerEl.style.isolation = "isolate"; + + containerEl.style.setProperty("overflow", "hidden"); + containerEl.style.setProperty("isolation", "isolate"); setOffset([containerEl.scrollLeft, containerEl.scrollTop]); } diff --git a/packages/reshaped/src/components/Overlay/Overlay.types.ts b/packages/reshaped/src/components/Overlay/Overlay.types.ts index abc4b995..39e3cc26 100644 --- a/packages/reshaped/src/components/Overlay/Overlay.types.ts +++ b/packages/reshaped/src/components/Overlay/Overlay.types.ts @@ -1,5 +1,5 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; -import type * as G from "types/global"; export type CloseReason = "overlay-click" | "escape-key"; @@ -29,7 +29,7 @@ export type Props = { /** Contain the component within the container element. Defaults to true when containerRef is provided */ contained?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Pagination/Pagination.types.ts b/packages/reshaped/src/components/Pagination/Pagination.types.ts index 754fea3d..91c349d5 100644 --- a/packages/reshaped/src/components/Pagination/Pagination.types.ts +++ b/packages/reshaped/src/components/Pagination/Pagination.types.ts @@ -1,4 +1,4 @@ -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; export type BaseProps = { /** Total number of pages available */ @@ -12,9 +12,9 @@ export type BaseProps = { /** aria-label attribute for the next page button */ nextAriaLabel: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/PinField/PinField.types.ts b/packages/reshaped/src/components/PinField/PinField.types.ts index b495da68..74619382 100644 --- a/packages/reshaped/src/components/PinField/PinField.types.ts +++ b/packages/reshaped/src/components/PinField/PinField.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type * as G from "types/global"; export type Size = "small" | "medium" | "large" | "xlarge"; @@ -16,11 +17,11 @@ type BaseProps = { /** Callback when the input value changes */ onChange?: G.ChangeHandler; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/PinField/PinFieldControlled.tsx b/packages/reshaped/src/components/PinField/PinFieldControlled.tsx index cb18d661..2f4c1b30 100644 --- a/packages/reshaped/src/components/PinField/PinFieldControlled.tsx +++ b/packages/reshaped/src/components/PinField/PinFieldControlled.tsx @@ -1,12 +1,12 @@ "use client"; +import { useHotkeys } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; import Text from "components/Text"; import View from "components/View"; import * as keys from "constants/keys"; -import useHotkeys from "hooks/useHotkeys"; import { onNextFrame } from "utilities/animation"; import { responsivePropDependency } from "utilities/props"; diff --git a/packages/reshaped/src/components/Popover/Popover.tsx b/packages/reshaped/src/components/Popover/Popover.tsx index cb6cebf5..15fdac08 100644 --- a/packages/reshaped/src/components/Popover/Popover.tsx +++ b/packages/reshaped/src/components/Popover/Popover.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import Dismissible, { type DismissibleProps } from "components/Dismissible"; import Flyout, { useFlyoutContext, type FlyoutProps } from "components/Flyout"; diff --git a/packages/reshaped/src/components/Progress/Progress.tsx b/packages/reshaped/src/components/Progress/Progress.tsx index 906d25de..4688d76c 100644 --- a/packages/reshaped/src/components/Progress/Progress.tsx +++ b/packages/reshaped/src/components/Progress/Progress.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import s from "./Progress.module.css"; diff --git a/packages/reshaped/src/components/Progress/Progress.types.ts b/packages/reshaped/src/components/Progress/Progress.types.ts index a0dba088..6f5e7bf3 100644 --- a/packages/reshaped/src/components/Progress/Progress.types.ts +++ b/packages/reshaped/src/components/Progress/Progress.types.ts @@ -1,4 +1,4 @@ -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; export type Props = { /** Value of the progress bar controlling the size of the fill */ @@ -16,7 +16,7 @@ export type Props = { /** aria-label attribute for the root element */ ariaLabel?: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.tsx b/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.tsx index 3fc5329d..d9d289d7 100644 --- a/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.tsx +++ b/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import s from "./ProgressIndicator.module.css"; diff --git a/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.types.ts b/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.types.ts index 0e1e6645..826a1d3c 100644 --- a/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.types.ts +++ b/packages/reshaped/src/components/ProgressIndicator/ProgressIndicator.types.ts @@ -1,4 +1,4 @@ -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; export type Props = { /** Total amount of progress indicator dots */ @@ -12,7 +12,7 @@ export type Props = { /** aria-label attribute for the root element */ ariaLabel?: string; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Radio/Radio.tsx b/packages/reshaped/src/components/Radio/Radio.tsx index 444ab015..8abd8aaa 100644 --- a/packages/reshaped/src/components/Radio/Radio.tsx +++ b/packages/reshaped/src/components/Radio/Radio.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/Radio/Radio.types.ts b/packages/reshaped/src/components/Radio/Radio.types.ts index 1a3b7609..7ee86be5 100644 --- a/packages/reshaped/src/components/Radio/Radio.types.ts +++ b/packages/reshaped/src/components/Radio/Radio.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -21,11 +22,11 @@ type BaseProps = { /** Callback when the input is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"label">; + attributes?: Attributes<"label">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; }; export type ControlledProps = BaseProps & { checked: boolean; defaultChecked?: never }; diff --git a/packages/reshaped/src/components/RadioGroup/RadioGroup.types.ts b/packages/reshaped/src/components/RadioGroup/RadioGroup.types.ts index f11d15c1..4f4c04c8 100644 --- a/packages/reshaped/src/components/RadioGroup/RadioGroup.types.ts +++ b/packages/reshaped/src/components/RadioGroup/RadioGroup.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { RadioProps } from "components/Radio"; import type React from "react"; import type * as G from "types/global"; @@ -16,9 +17,9 @@ type BaseProps = { /** Callback when the input value changes */ onChange?: G.ChangeHandler; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/Reshaped/Reshaped.tsx b/packages/reshaped/src/components/Reshaped/Reshaped.tsx index ca3a70eb..1e5c9cbe 100644 --- a/packages/reshaped/src/components/Reshaped/Reshaped.tsx +++ b/packages/reshaped/src/components/Reshaped/Reshaped.tsx @@ -1,17 +1,12 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { Reshaped as HeadlessReshaped, classNames } from "@reshaped/headless"; import React from "react"; import { GlobalColorMode, PrivateTheme } from "components/Theme"; import { useGlobalColorMode } from "components/Theme/useTheme"; import { ToastProvider } from "components/Toast"; -import { - SingletonEnvironmentContext, - useSingletonEnvironment, -} from "hooks/_private/useSingletonEnvironment"; -import { SingletonHotkeysProvider } from "hooks/_private/useSingletonHotkeys"; -import { SingletonKeyboardModeProvider } from "hooks/_private/useSingletonKeyboardMode"; +import { SingletonViewportProvider } from "hooks/_private/useSingletonViewport"; import s from "./Reshaped.module.css"; @@ -19,27 +14,14 @@ import type * as T from "./Reshaped.types"; import "./Reshaped.css"; -const ReshapedInner: React.FC = (props) => { - const { children, defaultRTL, defaultViewport = "s", toastOptions } = props; - const rtlState = useSingletonEnvironment(defaultRTL); - - return ( - - - - {children} - - - - ); -}; - const Reshaped: React.FC = (props) => { const { theme, defaultTheme = "reshaped", colorMode, defaultColorMode, + defaultViewport, + toastOptions, scoped, className, } = props; @@ -48,21 +30,25 @@ const Reshaped: React.FC = (props) => { const parentGlobalColorMode = useGlobalColorMode(); return ( - - + - {props.children} - - + + + {props.children} + + + + ); }; diff --git a/packages/reshaped/src/components/Reshaped/Reshaped.types.ts b/packages/reshaped/src/components/Reshaped/Reshaped.types.ts index d5a34daa..c49fe52f 100644 --- a/packages/reshaped/src/components/Reshaped/Reshaped.types.ts +++ b/packages/reshaped/src/components/Reshaped/Reshaped.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { GlobalColorModeProps, ThemeProps } from "components/Theme"; import type { ToastProviderProps } from "components/Toast"; import type React from "react"; @@ -23,7 +24,7 @@ export type Props = { /** Enable scoped mode for applications not using Reshaped provider at the application root */ scoped?: boolean; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Resizable/Resizable.tsx b/packages/reshaped/src/components/Resizable/Resizable.tsx index ae7fe05a..0958747c 100644 --- a/packages/reshaped/src/components/Resizable/Resizable.tsx +++ b/packages/reshaped/src/components/Resizable/Resizable.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Resizable/ResizableHandle.tsx b/packages/reshaped/src/components/Resizable/ResizableHandle.tsx index 0d9bc247..373131e4 100644 --- a/packages/reshaped/src/components/Resizable/ResizableHandle.tsx +++ b/packages/reshaped/src/components/Resizable/ResizableHandle.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Scrim/Scrim.tsx b/packages/reshaped/src/components/Scrim/Scrim.tsx index 62e37644..010265c7 100644 --- a/packages/reshaped/src/components/Scrim/Scrim.tsx +++ b/packages/reshaped/src/components/Scrim/Scrim.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Scrim/Scrim.types.ts b/packages/reshaped/src/components/Scrim/Scrim.types.ts index 43fb798d..76308ecc 100644 --- a/packages/reshaped/src/components/Scrim/Scrim.types.ts +++ b/packages/reshaped/src/components/Scrim/Scrim.types.ts @@ -1,7 +1,7 @@ import React from "react"; +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ViewProps } from "components/View"; -import type * as G from "types/global"; export type Props = { /** Node for inserting content */ @@ -11,9 +11,9 @@ export type Props = { /** Component content position */ position?: "full" | "top" | "bottom" | "start" | "end"; /** Additional classname for the scrim element */ - scrimClassName?: G.ClassName; + scrimClassName?: ClassName; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; } & Pick; diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx index 35490ed3..63035a08 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.tsx @@ -1,11 +1,10 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { disableScroll, enableScroll } from "@reshaped/utilities/internal"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { disableScroll, enableScroll } from "@reshaped/headless/internal"; import React, { forwardRef } from "react"; import useHandlerRef from "hooks/useHandlerRef"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import { resolveMixin } from "styles/mixin"; import s from "./ScrollArea.module.css"; diff --git a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts index 6455a09a..1e7f6fd3 100644 --- a/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts +++ b/packages/reshaped/src/components/ScrollArea/ScrollArea.types.ts @@ -1,3 +1,5 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; +import type { Coordinates } from "@reshaped/headless/internal"; import type React from "react"; import type * as G from "types/global"; @@ -7,15 +9,15 @@ export type Props = { /** Scrollbar visibility behavior based on the user interaction */ scrollbarDisplay?: "visible" | "hover" | "hidden"; /** Callback when the scroll area is scrolled */ - onScroll?: (args: G.Coordinates) => void; + onScroll?: (args: Coordinates) => void; /** Height of the scroll area, literal css value or unit token multiplier */ height?: G.Responsive; /** Maximum height of the scroll area, literal css value or unit token multiplier */ maxHeight?: G.Responsive; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type BarProps = { diff --git a/packages/reshaped/src/components/Select/Select.types.ts b/packages/reshaped/src/components/Select/Select.types.ts index 49648ab0..190a038f 100644 --- a/packages/reshaped/src/components/Select/Select.types.ts +++ b/packages/reshaped/src/components/Select/Select.types.ts @@ -1,8 +1,8 @@ import React from "react"; -import { DropdownMenuProps } from "components/DropdownMenu"; - +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ActionableProps } from "components/Actionable"; +import type { DropdownMenuProps } from "components/DropdownMenu"; import type { IconProps } from "components/Icon"; import type { MenuItemProps } from "components/MenuItem"; import type * as G from "types/global"; @@ -61,9 +61,9 @@ type BaseFragment = { /** Callback when the trigger is clicked */ onClick?: ActionableProps["onClick"]; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; /** Node for inserting children */ children?: React.ReactNode; }; @@ -89,7 +89,7 @@ export type NativeFragment = { /** Callback when the input is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"select">; + inputAttributes?: Attributes<"select">; }; export type NativeControlledFragment = { diff --git a/packages/reshaped/src/components/Select/SelectNative.tsx b/packages/reshaped/src/components/Select/SelectNative.tsx index 8e01723e..7c22982a 100644 --- a/packages/reshaped/src/components/Select/SelectNative.tsx +++ b/packages/reshaped/src/components/Select/SelectNative.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import s from "./Select.module.css"; diff --git a/packages/reshaped/src/components/Select/SelectRoot.tsx b/packages/reshaped/src/components/Select/SelectRoot.tsx index ef8dc4e9..b1ee8d2c 100644 --- a/packages/reshaped/src/components/Select/SelectRoot.tsx +++ b/packages/reshaped/src/components/Select/SelectRoot.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/Skeleton/Skeleton.tsx b/packages/reshaped/src/components/Skeleton/Skeleton.tsx index 42a04b3f..919f77b5 100644 --- a/packages/reshaped/src/components/Skeleton/Skeleton.tsx +++ b/packages/reshaped/src/components/Skeleton/Skeleton.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Skeleton/Skeleton.types.ts b/packages/reshaped/src/components/Skeleton/Skeleton.types.ts index 23bd94ba..5ed19b4a 100644 --- a/packages/reshaped/src/components/Skeleton/Skeleton.types.ts +++ b/packages/reshaped/src/components/Skeleton/Skeleton.types.ts @@ -1,9 +1,9 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { ViewProps } from "components/View"; -import type * as G from "types/global"; export type Props = Pick & { /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/Slider/Slider.types.ts b/packages/reshaped/src/components/Slider/Slider.types.ts index b94741f5..164889ed 100644 --- a/packages/reshaped/src/components/Slider/Slider.types.ts +++ b/packages/reshaped/src/components/Slider/Slider.types.ts @@ -1,4 +1,4 @@ -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; export type SingleChangeArgs = { /** Value of the slider, enables controlled mode */ @@ -140,9 +140,9 @@ type BaseProps = { /** Render a custom value in the thumb tooltip */ renderValue?: ((args: { value: number }) => string) | false; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ControlledProps = BaseProps & diff --git a/packages/reshaped/src/components/Slider/SliderControlled.tsx b/packages/reshaped/src/components/Slider/SliderControlled.tsx index cc0560ca..599f4a66 100644 --- a/packages/reshaped/src/components/Slider/SliderControlled.tsx +++ b/packages/reshaped/src/components/Slider/SliderControlled.tsx @@ -1,13 +1,12 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { disableScroll, enableScroll } from "@reshaped/utilities/internal"; +import { classNames, useRTL } from "@reshaped/headless"; +import { disableScroll, enableScroll } from "@reshaped/headless/internal"; import React from "react"; import { useFormControl } from "components/FormControl"; import useElementId from "hooks/useElementId"; import useHandlerRef from "hooks/useHandlerRef"; -import useRTL from "hooks/useRTL"; import s from "./Slider.module.css"; import { applyStepToValue, getDragCoord, triggerChangeEvent } from "./Slider.utilities"; diff --git a/packages/reshaped/src/components/Slider/SliderThumb.tsx b/packages/reshaped/src/components/Slider/SliderThumb.tsx index 30dba364..0244397b 100644 --- a/packages/reshaped/src/components/Slider/SliderThumb.tsx +++ b/packages/reshaped/src/components/Slider/SliderThumb.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import Text from "components/Text"; diff --git a/packages/reshaped/src/components/Stepper/Stepper.types.ts b/packages/reshaped/src/components/Stepper/Stepper.types.ts index f984872f..3b70bbe4 100644 --- a/packages/reshaped/src/components/Stepper/Stepper.types.ts +++ b/packages/reshaped/src/components/Stepper/Stepper.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -13,9 +14,9 @@ export type Props = { /** Gap between items (default: 3) */ gap?: G.Responsive; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ItemProps = { @@ -30,9 +31,9 @@ export type ItemProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ItemPrivateProps = ItemProps & { diff --git a/packages/reshaped/src/components/Switch/Switch.tsx b/packages/reshaped/src/components/Switch/Switch.tsx index 1b40edde..8270779e 100644 --- a/packages/reshaped/src/components/Switch/Switch.tsx +++ b/packages/reshaped/src/components/Switch/Switch.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/Switch/Switch.types.ts b/packages/reshaped/src/components/Switch/Switch.types.ts index b9f7bbb4..6c3fb2bc 100644 --- a/packages/reshaped/src/components/Switch/Switch.types.ts +++ b/packages/reshaped/src/components/Switch/Switch.types.ts @@ -1,5 +1,6 @@ import React from "react"; +import type { Attributes, ClassName } from "@reshaped/headless"; import type * as G from "types/global"; type BaseProps = { @@ -22,11 +23,11 @@ type BaseProps = { /** Callback when the switch is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"label">; + attributes?: Attributes<"label">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; }; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/Table/Table.tsx b/packages/reshaped/src/components/Table/Table.tsx index f7be215e..4f69139f 100644 --- a/packages/reshaped/src/components/Table/Table.tsx +++ b/packages/reshaped/src/components/Table/Table.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React, { isValidElement } from "react"; import useFadeSide from "hooks/_private/useFadeSide"; diff --git a/packages/reshaped/src/components/Table/Table.types.ts b/packages/reshaped/src/components/Table/Table.types.ts index 334f203b..64c73c29 100644 --- a/packages/reshaped/src/components/Table/Table.types.ts +++ b/packages/reshaped/src/components/Table/Table.types.ts @@ -1,6 +1,6 @@ import React from "react"; -import type * as G from "types/global"; +import type { Attributes, ClassName } from "@reshaped/headless"; export type Props = { /** Add border around the table */ @@ -10,9 +10,9 @@ export type Props = { /** Node for inserting children */ children: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type RowProps = { @@ -23,9 +23,9 @@ export type RowProps = { /** Node for inserting children */ children: React.ReactNode; /** Additional classname for the row element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the row element */ - attributes?: G.Attributes<"tr">; + attributes?: Attributes<"tr">; }; export type CellProps = { @@ -50,32 +50,32 @@ export type CellProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the cell element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the cell element */ - attributes?: G.Attributes<"td">; + attributes?: Attributes<"td">; }; export type HeadingProps = CellProps & { /** Additional attributes for the heading element */ - attributes?: G.Attributes<"th">; + attributes?: Attributes<"th">; }; export type BodyProps = { /** Node for inserting children */ children: React.ReactNode; /** Additional classname for the body element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the body element */ - attributes?: G.Attributes<"tbody">; + attributes?: Attributes<"tbody">; }; export type HeadProps = { /** Node for inserting children */ children: React.ReactNode; /** Additional classname for the head element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the head element */ - attributes?: G.Attributes<"thead">; + attributes?: Attributes<"thead">; }; export type PrivateCellProps = CellProps & { diff --git a/packages/reshaped/src/components/Tabs/Tabs.types.ts b/packages/reshaped/src/components/Tabs/Tabs.types.ts index 1bc8be17..f65ab41d 100644 --- a/packages/reshaped/src/components/Tabs/Tabs.types.ts +++ b/packages/reshaped/src/components/Tabs/Tabs.types.ts @@ -1,6 +1,6 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { IconProps } from "components/Icon"; import type React from "react"; -import type * as G from "types/global"; export type SelectionState = { left: number; @@ -22,16 +22,16 @@ export type ItemProps = { /** Disable the item user interaction */ disabled?: boolean; /** Additional attributes for the item element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ListProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the list element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the list element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type PanelProps = { @@ -40,9 +40,9 @@ export type PanelProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the panel element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the panel element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type BaseProps = { diff --git a/packages/reshaped/src/components/Tabs/TabsItem.tsx b/packages/reshaped/src/components/Tabs/TabsItem.tsx index 7e33fc0f..e405a826 100644 --- a/packages/reshaped/src/components/Tabs/TabsItem.tsx +++ b/packages/reshaped/src/components/Tabs/TabsItem.tsx @@ -1,14 +1,13 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { findParent } from "@reshaped/utilities/internal"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { findParent } from "@reshaped/headless/internal"; import React from "react"; import Actionable, { type ActionableRef } from "components/Actionable"; import HiddenInput from "components/HiddenInput"; import Icon from "components/Icon"; import Text from "components/Text"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import s from "./Tabs.module.css"; import { useTabs } from "./TabsContext"; diff --git a/packages/reshaped/src/components/Tabs/TabsList.tsx b/packages/reshaped/src/components/Tabs/TabsList.tsx index 356f1dca..7423126e 100644 --- a/packages/reshaped/src/components/Tabs/TabsList.tsx +++ b/packages/reshaped/src/components/Tabs/TabsList.tsx @@ -1,14 +1,12 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useIsomorphicLayoutEffect, useRTL } from "@reshaped/headless"; import React from "react"; import Actionable from "components/Actionable"; import Icon from "components/Icon"; import useFadeSide from "hooks/_private/useFadeSide"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import useKeyboardArrowNavigation from "hooks/useKeyboardArrowNavigation"; -import useRTL from "hooks/useRTL"; import IconChevronLeft from "icons/ChevronLeft"; import IconChevronRight from "icons/ChevronRight"; diff --git a/packages/reshaped/src/components/Tabs/TabsPanel.tsx b/packages/reshaped/src/components/Tabs/TabsPanel.tsx index 2f833f97..044f91b0 100644 --- a/packages/reshaped/src/components/Tabs/TabsPanel.tsx +++ b/packages/reshaped/src/components/Tabs/TabsPanel.tsx @@ -1,7 +1,7 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { getFocusableElements } from "@reshaped/utilities/internal"; +import { classNames } from "@reshaped/headless"; +import { getFocusableElements } from "@reshaped/headless/internal"; import React from "react"; import s from "./Tabs.module.css"; diff --git a/packages/reshaped/src/components/Text/Text.tsx b/packages/reshaped/src/components/Text/Text.tsx index ed425499..b8aac8f3 100644 --- a/packages/reshaped/src/components/Text/Text.tsx +++ b/packages/reshaped/src/components/Text/Text.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import { resolveMixin } from "styles/mixin"; import { responsiveClassNames } from "utilities/props"; diff --git a/packages/reshaped/src/components/Text/Text.types.ts b/packages/reshaped/src/components/Text/Text.types.ts index 2d32db14..3e14e35b 100644 --- a/packages/reshaped/src/components/Text/Text.types.ts +++ b/packages/reshaped/src/components/Text/Text.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as G from "types/global"; @@ -46,9 +47,9 @@ export type Props; + attributes?: Attributes; /** Render as a different html tag */ as?: TagName extends keyof React.JSX.IntrinsicElements ? TagName diff --git a/packages/reshaped/src/components/TextArea/TextArea.tsx b/packages/reshaped/src/components/TextArea/TextArea.tsx index 77a35165..dca3516b 100644 --- a/packages/reshaped/src/components/TextArea/TextArea.tsx +++ b/packages/reshaped/src/components/TextArea/TextArea.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/TextArea/TextArea.types.ts b/packages/reshaped/src/components/TextArea/TextArea.types.ts index 9d049d19..18f1609d 100644 --- a/packages/reshaped/src/components/TextArea/TextArea.types.ts +++ b/packages/reshaped/src/components/TextArea/TextArea.types.ts @@ -1,7 +1,7 @@ import React from "react"; -import { type FormControlProps } from "components/FormControl"; - +import type { Attributes, ClassName } from "@reshaped/headless"; +import type { FormControlProps } from "components/FormControl"; import type * as G from "types/global"; type Size = G.Responsive<"medium" | "large" | "xlarge">; @@ -30,11 +30,11 @@ type BaseProps = { /** Callback when the text area is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"textarea">; + inputAttributes?: Attributes<"textarea">; /** Enable or disable the text area resizing, supports auto-sizing */ resize?: "none" | "auto"; } & Pick; diff --git a/packages/reshaped/src/components/TextField/TextField.tsx b/packages/reshaped/src/components/TextField/TextField.tsx index 543053d6..edf0d875 100644 --- a/packages/reshaped/src/components/TextField/TextField.tsx +++ b/packages/reshaped/src/components/TextField/TextField.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React from "react"; import { useFormControl } from "components/FormControl"; diff --git a/packages/reshaped/src/components/TextField/TextField.types.ts b/packages/reshaped/src/components/TextField/TextField.types.ts index bb412764..620ebee9 100644 --- a/packages/reshaped/src/components/TextField/TextField.types.ts +++ b/packages/reshaped/src/components/TextField/TextField.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { FormControlProps } from "components/FormControl"; import type { IconProps } from "components/Icon"; import type React from "react"; @@ -51,11 +52,11 @@ export type BaseProps = { /** Callback when the text field is blurred */ onBlur?: (e: React.FocusEvent) => void; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; /** Additional attributes for the input element */ - inputAttributes?: G.Attributes<"input">; + inputAttributes?: Attributes<"input">; } & Pick; export type ControlledProps = BaseProps & { diff --git a/packages/reshaped/src/components/Theme/GlobalColorMode.tsx b/packages/reshaped/src/components/Theme/GlobalColorMode.tsx index 20378025..6cd52223 100644 --- a/packages/reshaped/src/components/Theme/GlobalColorMode.tsx +++ b/packages/reshaped/src/components/Theme/GlobalColorMode.tsx @@ -1,8 +1,8 @@ "use client"; +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import { enableTransitions, disableTransitions, onNextFrame } from "utilities/animation"; import { GlobalColorModeContext } from "./Theme.context"; diff --git a/packages/reshaped/src/components/Theme/Theme.tsx b/packages/reshaped/src/components/Theme/Theme.tsx index 29be34e9..02de409a 100644 --- a/packages/reshaped/src/components/Theme/Theme.tsx +++ b/packages/reshaped/src/components/Theme/Theme.tsx @@ -1,10 +1,8 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames, useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; - import { ThemeContext } from "./Theme.context"; import s from "./Theme.module.css"; import * as T from "./Theme.types"; @@ -32,6 +30,8 @@ export const PrivateTheme: React.FC = (props) => { const usedColorMode = colorMode === "inverted" ? invertedColorMode : colorMode || parentColorMode; const rootClassNames = classNames(s.root, className); + console.log(defaultName, "defaultName2"); + const setRootTheme: T.ThemeContextData["setRootTheme"] = React.useCallback( (theme) => { if (isRootProvider) { diff --git a/packages/reshaped/src/components/Theme/Theme.types.ts b/packages/reshaped/src/components/Theme/Theme.types.ts index b73c8fc8..ae4a10ca 100644 --- a/packages/reshaped/src/components/Theme/Theme.types.ts +++ b/packages/reshaped/src/components/Theme/Theme.types.ts @@ -2,6 +2,8 @@ import React from "react"; import * as G from "types/global"; +import type { ClassName } from "@reshaped/headless"; + export type GlobalColorModeContextData = { mode: G.ColorMode; setMode: (mode: G.ColorMode) => void; @@ -24,7 +26,7 @@ export type Props = { /** Color mode to use */ colorMode?: G.ColorMode | "inverted"; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Node for inserting children */ children?: React.ReactNode; }; diff --git a/packages/reshaped/src/components/Timeline/Timeline.tsx b/packages/reshaped/src/components/Timeline/Timeline.tsx index c78efd9e..8a051432 100644 --- a/packages/reshaped/src/components/Timeline/Timeline.tsx +++ b/packages/reshaped/src/components/Timeline/Timeline.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React, { isValidElement } from "react"; import View from "components/View"; diff --git a/packages/reshaped/src/components/Timeline/Timeline.types.ts b/packages/reshaped/src/components/Timeline/Timeline.types.ts index 438a3ffc..c039dfa7 100644 --- a/packages/reshaped/src/components/Timeline/Timeline.types.ts +++ b/packages/reshaped/src/components/Timeline/Timeline.types.ts @@ -1,13 +1,13 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; -import type * as G from "types/global"; export type Props = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"ul">; + attributes?: Attributes<"ul">; }; export type ItemProps = { @@ -16,7 +16,7 @@ export type ItemProps = { /** Node for inserting children */ children?: React.ReactNode; /** Additional classname for the item element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the item element */ - attributes?: G.Attributes<"li">; + attributes?: Attributes<"li">; }; diff --git a/packages/reshaped/src/components/Toast/Toast.types.ts b/packages/reshaped/src/components/Toast/Toast.types.ts index f08f838b..5e239cf8 100644 --- a/packages/reshaped/src/components/Toast/Toast.types.ts +++ b/packages/reshaped/src/components/Toast/Toast.types.ts @@ -1,6 +1,6 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type { IconProps } from "components/Icon"; import type React from "react"; -import type * as G from "types/global"; export type Status = "entering" | "entered" | "exited"; export type TimeoutAlias = "short" | "long"; @@ -29,9 +29,9 @@ export type Props = { */ color?: "neutral" | "primary" | "critical" | "positive" | "warning" | "inverted"; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; export type ProviderProps = { diff --git a/packages/reshaped/src/components/Toast/ToastContainer.tsx b/packages/reshaped/src/components/Toast/ToastContainer.tsx index 555ce58f..d3a9775e 100644 --- a/packages/reshaped/src/components/Toast/ToastContainer.tsx +++ b/packages/reshaped/src/components/Toast/ToastContainer.tsx @@ -1,8 +1,7 @@ "use client"; -import { TrapFocus } from "@reshaped/utilities"; -import { classNames } from "@reshaped/utilities"; -import { checkKeyboardMode } from "@reshaped/utilities/internal"; +import { TrapFocus, classNames } from "@reshaped/headless"; +import { checkKeyboardMode } from "@reshaped/headless/internal"; import React from "react"; import { onNextFrame } from "utilities/animation"; diff --git a/packages/reshaped/src/components/Toast/ToastRegion.tsx b/packages/reshaped/src/components/Toast/ToastRegion.tsx index cf400349..8384435a 100644 --- a/packages/reshaped/src/components/Toast/ToastRegion.tsx +++ b/packages/reshaped/src/components/Toast/ToastRegion.tsx @@ -1,7 +1,7 @@ "use client"; -import { classNames } from "@reshaped/utilities"; -import { focusableSelector } from "@reshaped/utilities/internal"; +import { classNames } from "@reshaped/headless"; +import { focusableSelector } from "@reshaped/headless/internal"; import React from "react"; import ToastContext from "./Toast.context"; diff --git a/packages/reshaped/src/components/View/View.tsx b/packages/reshaped/src/components/View/View.tsx index c15be428..e464a003 100644 --- a/packages/reshaped/src/components/View/View.tsx +++ b/packages/reshaped/src/components/View/View.tsx @@ -1,4 +1,4 @@ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React, { isValidElement } from "react"; import Divider, { type DividerProps } from "components/Divider"; diff --git a/packages/reshaped/src/components/View/View.types.ts b/packages/reshaped/src/components/View/View.types.ts index ee22fb8d..9d8839bf 100644 --- a/packages/reshaped/src/components/View/View.types.ts +++ b/packages/reshaped/src/components/View/View.types.ts @@ -1,3 +1,4 @@ +import type { Attributes, ClassName } from "@reshaped/headless"; import type React from "react"; import type * as TStyles from "styles/types"; import type * as G from "types/global"; @@ -117,9 +118,9 @@ export type Props; + attributes?: Attributes; } & Pick; export type ItemProps = { @@ -138,9 +139,9 @@ export type ItemProps; + attributes?: Attributes; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Node for inserting the item content */ children?: React.ReactNode; }; diff --git a/packages/reshaped/src/components/_private/Aligner/Aligner.tsx b/packages/reshaped/src/components/_private/Aligner/Aligner.tsx index 11339a3b..a2b71824 100644 --- a/packages/reshaped/src/components/_private/Aligner/Aligner.tsx +++ b/packages/reshaped/src/components/_private/Aligner/Aligner.tsx @@ -7,7 +7,7 @@ * on the --rs-p, --rs-p-v and --rs-p-h css variables */ -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import s from "./Aligner.module.css"; diff --git a/packages/reshaped/src/components/_private/Aligner/Aligner.types.ts b/packages/reshaped/src/components/_private/Aligner/Aligner.types.ts index 0662cfef..d56e2c94 100644 --- a/packages/reshaped/src/components/_private/Aligner/Aligner.types.ts +++ b/packages/reshaped/src/components/_private/Aligner/Aligner.types.ts @@ -1,4 +1,4 @@ -import type * as G from "types/global"; +import type { ClassName, Attributes } from "@reshaped/headless"; type Side = "start" | "end" | "top" | "bottom" | "inline" | "block" | "all"; @@ -10,7 +10,7 @@ export type Props = { */ side?: Side | Side[]; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/_private/Expandable/Expandable.tsx b/packages/reshaped/src/components/_private/Expandable/Expandable.tsx index abf4736a..24dbc85a 100644 --- a/packages/reshaped/src/components/_private/Expandable/Expandable.tsx +++ b/packages/reshaped/src/components/_private/Expandable/Expandable.tsx @@ -1,9 +1,9 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; +import { classNames } from "@reshaped/headless"; import React from "react"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import { onNextFrame } from "utilities/animation"; import s from "./Expandable.module.css"; diff --git a/packages/reshaped/src/components/_private/Expandable/Expandable.types.ts b/packages/reshaped/src/components/_private/Expandable/Expandable.types.ts index 514d462c..09825609 100644 --- a/packages/reshaped/src/components/_private/Expandable/Expandable.types.ts +++ b/packages/reshaped/src/components/_private/Expandable/Expandable.types.ts @@ -1,9 +1,9 @@ import React from "react"; -import * as G from "types/global"; +import type { Attributes } from "@reshaped/headless"; export type ContentProps = { active?: boolean; children?: React.ReactNode; - attributes?: G.Attributes<"div">; + attributes?: Attributes<"div">; }; diff --git a/packages/reshaped/src/components/_private/Portal/Portal.tsx b/packages/reshaped/src/components/_private/Portal/Portal.tsx index 708d05ac..0f29c174 100644 --- a/packages/reshaped/src/components/_private/Portal/Portal.tsx +++ b/packages/reshaped/src/components/_private/Portal/Portal.tsx @@ -1,10 +1,10 @@ "use client"; +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; import ReactDOM from "react-dom"; import Theme from "components/Theme"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; import useToggle from "hooks/useToggle"; import s from "./Portal.module.css"; diff --git a/packages/reshaped/src/core/Actionable/Actionable.tsx b/packages/reshaped/src/core/Actionable/Actionable.tsx index 2b948f9b..ab6f390b 100644 --- a/packages/reshaped/src/core/Actionable/Actionable.tsx +++ b/packages/reshaped/src/core/Actionable/Actionable.tsx @@ -1,6 +1,6 @@ "use client"; -import { classNames } from "@reshaped/utilities"; +import { classNames } from "@reshaped/headless"; import React, { forwardRef } from "react"; import * as keys from "constants/keys"; diff --git a/packages/reshaped/src/core/Actionable/Actionable.types.ts b/packages/reshaped/src/core/Actionable/Actionable.types.ts index 857fbad5..137ad08a 100644 --- a/packages/reshaped/src/core/Actionable/Actionable.types.ts +++ b/packages/reshaped/src/core/Actionable/Actionable.types.ts @@ -1,13 +1,13 @@ +import type { Attributes as AttributesType, ClassName } from "@reshaped/headless"; import type React from "react"; -import type * as G from "types/global"; export type AttributesRef = React.RefObject; -type Attributes = G.Attributes<"button"> & - Omit> & { +type Attributes = AttributesType<"button"> & + Omit> & { ref?: AttributesRef; }; -export type RenderAttributes = G.Attributes<"a"> & { +export type RenderAttributes = AttributesType<"a"> & { ref: React.RefObject; children: React.ReactNode; }; @@ -30,7 +30,7 @@ export type Props = { /** Render as a different element */ as?: keyof React.JSX.IntrinsicElements; /** Additional classname for the root element */ - className?: G.ClassName; + className?: ClassName; /** Additional attributes for the root element */ attributes?: Attributes; }; diff --git a/packages/reshaped/src/hooks/_private/useDrag.ts b/packages/reshaped/src/hooks/_private/useDrag.ts index 26d242fc..a6cff0c1 100644 --- a/packages/reshaped/src/hooks/_private/useDrag.ts +++ b/packages/reshaped/src/hooks/_private/useDrag.ts @@ -1,11 +1,11 @@ "use client"; -import { disableScroll, enableScroll } from "@reshaped/utilities/internal"; +import { useHotkeys } from "@reshaped/headless"; +import { disableScroll, enableScroll } from "@reshaped/headless/internal"; import React from "react"; import * as keys from "constants/keys"; import useHandlerRef from "hooks/useHandlerRef"; -import useHotkeys from "hooks/useHotkeys"; import useToggle from "hooks/useToggle"; export type UseDragCallbackArgs = { x: number; y: number; triggerX: number; triggerY: number }; diff --git a/packages/reshaped/src/hooks/_private/useFadeSide.ts b/packages/reshaped/src/hooks/_private/useFadeSide.ts index 53810436..4fb4e1db 100644 --- a/packages/reshaped/src/hooks/_private/useFadeSide.ts +++ b/packages/reshaped/src/hooks/_private/useFadeSide.ts @@ -1,11 +1,9 @@ "use client"; -import { rafThrottle } from "@reshaped/utilities/internal"; +import { useIsomorphicLayoutEffect, useRTL } from "@reshaped/headless"; +import { rafThrottle } from "@reshaped/headless/internal"; import React from "react"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; -import useRTL from "hooks/useRTL"; - const useFadeSide = ( scrollableRef: React.RefObject, options: { disabled?: boolean } = {} diff --git a/packages/reshaped/src/hooks/_private/useSingletonEnvironment.ts b/packages/reshaped/src/hooks/_private/useSingletonEnvironment.ts deleted file mode 100644 index 4153d240..00000000 --- a/packages/reshaped/src/hooks/_private/useSingletonEnvironment.ts +++ /dev/null @@ -1,43 +0,0 @@ -import React from "react"; - -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; - -import type * as G from "types/global"; - -type Context = { rtl: [boolean, (state: boolean) => void]; defaultViewport: G.Viewport }; - -export const SingletonEnvironmentContext = React.createContext({ - rtl: [false, () => {}], - defaultViewport: "s", -}); - -export const useSingletonEnvironment = (defaultRTL?: boolean) => { - const state = React.useState(defaultRTL || false); - const [isRTL, setRTL] = state; - - /** - * Handle changing dir attribute directly - */ - useIsomorphicLayoutEffect(() => { - const observer = new MutationObserver((mutations) => { - mutations.forEach((mutation) => { - if (mutation.attributeName !== "dir") return; - - const nextRTL = (mutation.target as HTMLElement).dir === "rtl"; - if (isRTL !== nextRTL) setRTL(nextRTL); - }); - }); - - observer.observe(document.documentElement, { attributes: true }); - return () => observer.disconnect(); - }, [isRTL]); - - /** - * Handle setRTL usage - */ - useIsomorphicLayoutEffect(() => { - document.documentElement.setAttribute("dir", isRTL ? "rtl" : "ltr"); - }, [isRTL]); - - return state; -}; diff --git a/packages/reshaped/src/hooks/_private/useSingletonViewport.tsx b/packages/reshaped/src/hooks/_private/useSingletonViewport.tsx new file mode 100644 index 00000000..cd0f3560 --- /dev/null +++ b/packages/reshaped/src/hooks/_private/useSingletonViewport.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; +import React from "react"; + +import defaultBreakpoints from "constants/breakpoints"; + +import type * as G from "types/global"; + +const SingletonViewportContext = React.createContext<{ + viewport: G.Viewport; +}>({ + viewport: "s", +}); + +export const SingletonViewportProvider: React.FC<{ + children: React.ReactNode; + defaultViewport?: G.Viewport; +}> = (props) => { + const { children, defaultViewport = "s" } = props; + const [viewport, setViewport] = React.useState(defaultViewport); + + useIsomorphicLayoutEffect(() => { + const rootThemeEl = document.querySelector("[data-rs-theme]"); + const rootStyle = rootThemeEl && window.getComputedStyle(rootThemeEl); + + /** + * We generate variables for the viewport breakpoints in the themes + * We use them here in case they're custom and fallback to the default values + * in case there is no SSR passing the data-rs-theme attribute + */ + const breakpoints = { + m: + (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-m-min"))) || + defaultBreakpoints.m, + l: + (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-l-min"))) || + defaultBreakpoints.l, + xl: + (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-xl-min"))) || + defaultBreakpoints.xl, + }; + + const mediaQueries = { + s: `(max-width: ${breakpoints.m - 1}px)`, + m: `(min-width: ${breakpoints.m}px) and (max-width: ${breakpoints.l - 1}px)`, + l: `(min-width: ${breakpoints.l}px) and (max-width: ${breakpoints.xl - 1}px)`, + xl: `(min-width: ${breakpoints.xl}px)`, + }; + + const viewports = Object.keys(mediaQueries) as (keyof typeof mediaQueries)[]; + const matchers = viewports.map((viewport) => { + const mq = window.matchMedia(mediaQueries[viewport]); + return { mq, handler: () => mq.matches && setViewport(viewport) }; + }); + + matchers.forEach(({ handler, mq }) => { + handler(); + mq.addEventListener("change", handler); + }); + + return () => { + matchers.forEach(({ handler, mq }) => { + mq.removeEventListener("change", handler); + }); + }; + }, []); + + return ( + + {children} + + ); +}; + +const useSingletonViewport = () => React.useContext(SingletonViewportContext); + +export default useSingletonViewport; diff --git a/packages/reshaped/src/hooks/tests/useHotkeys.stories.tsx b/packages/reshaped/src/hooks/tests/useHotkeys.stories.tsx index a46863fc..a24d80c9 100644 --- a/packages/reshaped/src/hooks/tests/useHotkeys.stories.tsx +++ b/packages/reshaped/src/hooks/tests/useHotkeys.stories.tsx @@ -1,8 +1,8 @@ +import { useHotkeys } from "@reshaped/headless"; import { StoryObj } from "@storybook/react-vite"; import { expect, fn, userEvent } from "storybook/test"; import View from "components/View"; -import useHotkeys from "hooks/useHotkeys"; export default { title: "Hooks/useHotkeys", diff --git a/packages/reshaped/src/hooks/tests/useKeyboardMode.stories.tsx b/packages/reshaped/src/hooks/tests/useKeyboardMode.stories.tsx index 94e9ed01..e547b978 100644 --- a/packages/reshaped/src/hooks/tests/useKeyboardMode.stories.tsx +++ b/packages/reshaped/src/hooks/tests/useKeyboardMode.stories.tsx @@ -1,9 +1,9 @@ +import { useKeyboardMode } from "@reshaped/headless"; import { StoryObj } from "@storybook/react-vite"; import { expect, userEvent } from "storybook/test"; import Button from "components/Button"; import View from "components/View"; -import useKeyboardMode from "hooks/useKeyboardMode"; export default { title: "Hooks/useKeyboardMode", diff --git a/packages/reshaped/src/hooks/tests/useRTL.stories.tsx b/packages/reshaped/src/hooks/tests/useRTL.stories.tsx index deb185de..b3f3566b 100644 --- a/packages/reshaped/src/hooks/tests/useRTL.stories.tsx +++ b/packages/reshaped/src/hooks/tests/useRTL.stories.tsx @@ -1,9 +1,8 @@ +import { useRTL } from "@reshaped/headless"; import { StoryObj } from "@storybook/react-vite"; import React from "react"; import { expect } from "storybook/test"; -import useRTL from "hooks/useRTL"; - export default { title: "Hooks/useRTL", parameters: { diff --git a/packages/reshaped/src/hooks/useHandlerRef.ts b/packages/reshaped/src/hooks/useHandlerRef.ts index e1e8f311..0dee1e5b 100644 --- a/packages/reshaped/src/hooks/useHandlerRef.ts +++ b/packages/reshaped/src/hooks/useHandlerRef.ts @@ -1,7 +1,6 @@ +import { useIsomorphicLayoutEffect } from "@reshaped/headless"; import React from "react"; -import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect"; - /** * Hook for wrapping event handlers passed as props with a ref * This way we can keep the instance of the ref the same and pass this ref to the effects dependency array diff --git a/packages/reshaped/src/hooks/useKeyboardArrowNavigation.tsx b/packages/reshaped/src/hooks/useKeyboardArrowNavigation.tsx index 8ef5704a..71ed5cf6 100644 --- a/packages/reshaped/src/hooks/useKeyboardArrowNavigation.tsx +++ b/packages/reshaped/src/hooks/useKeyboardArrowNavigation.tsx @@ -1,14 +1,13 @@ +import { useHotkeys } from "@reshaped/headless"; import { focusFirstElement, focusLastElement, focusNextElement, focusPreviousElement, getFocusableElements, -} from "@reshaped/utilities/internal"; +} from "@reshaped/headless/internal"; import React, { useEffect } from "react"; -import useHotkeys from "./useHotkeys"; - type Props = { ref: React.RefObject; disabled?: boolean; diff --git a/packages/reshaped/src/hooks/useKeyboardMode.ts b/packages/reshaped/src/hooks/useKeyboardMode.ts deleted file mode 100644 index e50f5c37..00000000 --- a/packages/reshaped/src/hooks/useKeyboardMode.ts +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import React from "react"; - -import { SingletonKeyboardModeContext } from "hooks/_private/useSingletonKeyboardMode"; - -const useKeyboardMode = () => { - const singletonKeyboardMode = React.useContext(SingletonKeyboardModeContext); - - return React.useMemo( - () => ({ - enable: singletonKeyboardMode.enable, - disable: singletonKeyboardMode.disable, - activate: singletonKeyboardMode.activate, - deactivate: singletonKeyboardMode.deactivate, - }), - [singletonKeyboardMode] - ); -}; - -export default useKeyboardMode; diff --git a/packages/reshaped/src/hooks/useRTL.ts b/packages/reshaped/src/hooks/useRTL.ts deleted file mode 100644 index bc60f54c..00000000 --- a/packages/reshaped/src/hooks/useRTL.ts +++ /dev/null @@ -1,11 +0,0 @@ -"use client"; - -import React from "react"; - -import { SingletonEnvironmentContext } from "hooks/_private/useSingletonEnvironment"; - -const useRTL = () => { - return React.useContext(SingletonEnvironmentContext).rtl; -}; - -export default useRTL; diff --git a/packages/reshaped/src/hooks/useResponsiveClientValue.ts b/packages/reshaped/src/hooks/useResponsiveClientValue.ts index 69642176..b84b080c 100644 --- a/packages/reshaped/src/hooks/useResponsiveClientValue.ts +++ b/packages/reshaped/src/hooks/useResponsiveClientValue.ts @@ -1,71 +1,24 @@ "use client"; -import React from "react"; +import { useMemo } from "react"; -import defaultBreakpoints from "constants/breakpoints"; -import { SingletonEnvironmentContext } from "hooks/_private/useSingletonEnvironment"; -import useIsomorphicLayoutEffect from "hooks/useIsomorphicLayoutEffect"; +import useViewport from "hooks/useViewport"; import type * as G from "types/global"; const useResponsiveClientValue = (value: G.Responsive): T | undefined => { - const { defaultViewport } = React.useContext(SingletonEnvironmentContext); - const [viewport, setViewport] = React.useState(defaultViewport); - - useIsomorphicLayoutEffect(() => { - const rootThemeEl = document.querySelector("[data-rs-theme]"); - const rootStyle = rootThemeEl && window.getComputedStyle(rootThemeEl); - - /** - * We generate variables for the viewport breakpoints in the themes - * We use them here in case they're custom and fallback to the default values - * in case there is no SSR passing the data-rs-theme attribute - */ - const breakpoints = { - m: - (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-m-min"))) || - defaultBreakpoints.m, - l: - (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-l-min"))) || - defaultBreakpoints.l, - xl: - (rootStyle && Number(rootStyle.getPropertyValue("--rs-viewport-xl-min"))) || - defaultBreakpoints.xl, - }; - - const mediaQueries = { - s: `(max-width: ${breakpoints.m - 1}px)`, - m: `(min-width: ${breakpoints.m}px) and (max-width: ${breakpoints.l - 1}px)`, - l: `(min-width: ${breakpoints.l}px) and (max-width: ${breakpoints.xl - 1}px)`, - xl: `(min-width: ${breakpoints.xl}px)`, - }; - - const viewports = Object.keys(mediaQueries) as (keyof typeof mediaQueries)[]; - const matchers = viewports.map((viewport) => { - const mq = window.matchMedia(mediaQueries[viewport]); - return { mq, handler: () => mq.matches && setViewport(viewport) }; - }); - - matchers.forEach(({ handler, mq }) => { - handler(); - mq.addEventListener("change", handler); - }); - - return () => { - matchers.forEach(({ handler, mq }) => { - mq.removeEventListener("change", handler); - }); - }; - }, []); - - if (typeof value !== "object" || value === null || !("s" in value)) { - return value as T; - } - - if (viewport === "xl") return value.xl ?? value.l ?? value.m ?? value.s; - if (viewport === "l") return value.l ?? value.m ?? value.s; - if (viewport === "m") return value.m ?? value.s; - return value.s; + const viewport = useViewport(); + + return useMemo(() => { + if (typeof value !== "object" || value === null || !("s" in value)) { + return value as T; + } + + if (viewport === "xl") return value.xl ?? value.l ?? value.m ?? value.s; + if (viewport === "l") return value.l ?? value.m ?? value.s; + if (viewport === "m") return value.m ?? value.s; + return value.s; + }, [viewport, value]); }; export default useResponsiveClientValue; diff --git a/packages/reshaped/src/hooks/useScrollLock.ts b/packages/reshaped/src/hooks/useScrollLock.ts index fecc7009..5014fe47 100644 --- a/packages/reshaped/src/hooks/useScrollLock.ts +++ b/packages/reshaped/src/hooks/useScrollLock.ts @@ -1,6 +1,6 @@ "use client"; -import { lockScroll } from "@reshaped/utilities"; +import { lockScroll } from "@reshaped/headless/internal"; import React from "react"; const useScrollLock = (options?: { diff --git a/packages/reshaped/src/hooks/useViewport.ts b/packages/reshaped/src/hooks/useViewport.ts new file mode 100644 index 00000000..69730ecb --- /dev/null +++ b/packages/reshaped/src/hooks/useViewport.ts @@ -0,0 +1,9 @@ +import useSingletonViewport from "hooks/_private/useSingletonViewport"; + +const useViewport = () => { + const { viewport } = useSingletonViewport(); + + return viewport; +}; + +export default useViewport; diff --git a/packages/reshaped/src/index.ts b/packages/reshaped/src/index.ts index 4d6b6410..c4f5ec54 100644 --- a/packages/reshaped/src/index.ts +++ b/packages/reshaped/src/index.ts @@ -204,17 +204,15 @@ export type { ViewProps, ViewItemProps } from "components/View"; /** * Hooks */ +export { useHotkeys, useIsomorphicLayoutEffect, useRTL, useKeyboardMode } from "@reshaped/headless"; + export { useFormControl } from "components/FormControl"; export { default as Theme, useTheme, type ThemeProps } from "components/Theme"; export { default as useHandlerRef } from "hooks/useHandlerRef"; -export { default as useHotkeys } from "hooks/useHotkeys"; -export { default as useIsomorphicLayoutEffect } from "hooks/useIsomorphicLayoutEffect"; -export { default as useKeyboardMode } from "hooks/useKeyboardMode"; export { default as useKeyboardArrowNavigation } from "hooks/useKeyboardArrowNavigation"; export { default as useOnClickOutside } from "hooks/useOnClickOutside"; export { default as useResponsiveClientValue } from "hooks/useResponsiveClientValue"; -export { default as useRTL } from "hooks/useRTL"; export { default as useScrollLock } from "hooks/useScrollLock"; export { default as useToggle } from "hooks/useToggle"; @@ -222,13 +220,14 @@ export { default as useToggle } from "hooks/useToggle"; * Utility functions */ export { responsivePropDependency } from "utilities/props"; -export { TrapFocus, classNames } from "@reshaped/utilities"; +export { TrapFocus, classNames } from "@reshaped/headless"; /** * Types */ +export type { Attributes } from "@reshaped/headless"; export type { ReshapedConfig } from "types/config"; -export type { Responsive, Attributes, ColorMode } from "types/global"; +export type { Responsive, ColorMode } from "types/global"; /** * Dev utilities diff --git a/packages/reshaped/src/styles/mixin.ts b/packages/reshaped/src/styles/mixin.ts index 43303cf0..90fb3693 100644 --- a/packages/reshaped/src/styles/mixin.ts +++ b/packages/reshaped/src/styles/mixin.ts @@ -37,8 +37,8 @@ import radius from "styles/resolvers/radius"; import textAlign from "styles/resolvers/textAlign"; import width from "styles/resolvers/width"; +import type { ClassName } from "@reshaped/headless"; import type { Mixin } from "styles/types"; -import type * as G from "types/global"; const mixinMap = { align, @@ -81,7 +81,7 @@ const mixinMap = { export const resolveMixin = (mixin: Mixin) => { const output = { variables: {} as React.CSSProperties, - classNames: [] as G.ClassName[], + classNames: [] as ClassName[], }; const entries = Object.entries(mixin); entries.forEach(([key, value]) => { diff --git a/packages/reshaped/src/styles/types.ts b/packages/reshaped/src/styles/types.ts index 4a93c0a0..9c5cee29 100644 --- a/packages/reshaped/src/styles/types.ts +++ b/packages/reshaped/src/styles/types.ts @@ -2,6 +2,8 @@ import React from "react"; import * as G from "types/global"; +import type { ClassName } from "@reshaped/headless"; + /* Values */ type Unit = number; type Literal = string; @@ -48,7 +50,7 @@ export type BorderColor = export type StyleResolver = (value?: G.Responsive) => { variables?: React.CSSProperties; - classNames?: G.ClassName; + classNames?: ClassName; }; export type Mixin = { diff --git a/packages/reshaped/src/types/global.ts b/packages/reshaped/src/types/global.ts index 41e4860c..6148ef1a 100644 --- a/packages/reshaped/src/types/global.ts +++ b/packages/reshaped/src/types/global.ts @@ -1,31 +1,9 @@ import React from "react"; -/** - * DOM - */ -type ClassNameValue = string | null | undefined | false; -export type ClassName = ClassNameValue | ClassNameValue[] | ClassName[]; - -export type CSSVariable = `--${string}`; -export type StyleAttribute = - | React.CSSProperties - | (React.CSSProperties & Record); - -type DataAttributes = object | Record<`data-${string}`, string | boolean>; - -export type Attributes = - // If tag name is not passed, fallback to HTMLElement attributes - (TagName extends keyof React.JSX.IntrinsicElements - ? React.JSX.IntrinsicElements[TagName] - : React.HTMLAttributes) & - DataAttributes & { style?: StyleAttribute }; - export type Viewport = "s" | "m" | "l" | "xl"; export type ResponsiveOnly = { [key in Viewport]?: T }; export type Responsive = T | ResponsiveOnly; -export type Coordinates = { x: number; y: number }; - /** * Form handlers */ diff --git a/packages/reshaped/src/utilities/props.ts b/packages/reshaped/src/utilities/props.ts index 960643fa..608ba123 100644 --- a/packages/reshaped/src/utilities/props.ts +++ b/packages/reshaped/src/utilities/props.ts @@ -1,3 +1,4 @@ +import type { CSSVariable } from "@reshaped/headless"; import type * as G from "types/global"; type Value = string | boolean | number | undefined; @@ -48,13 +49,13 @@ export const responsiveClassNames = >( }; export const responsiveVariables = ( - variableName: G.CSSVariable, + variableName: CSSVariable, value?: G.Responsive -): Record => { +): Record => { if (value === undefined) return {}; if (typeof value !== "object") return { [`${variableName}-s`]: value }; - return Object.keys(value).reduce>((acc, key) => { + return Object.keys(value).reduce>((acc, key) => { const viewportValue = value[key as G.Viewport]; if (viewportValue === undefined) return acc; diff --git a/packages/utilities/src/flyout/types.ts b/packages/utilities/src/flyout/types.ts index a7a869ba..17e57f62 100644 --- a/packages/utilities/src/flyout/types.ts +++ b/packages/utilities/src/flyout/types.ts @@ -1,9 +1,10 @@ +import type { Coordinates } from "@/types/global"; + type XSide = "start" | "end"; type YSide = "top" | "bottom"; export type Side = XSide | YSide; export type Position = `${YSide}` | `${YSide}-${XSide}` | `${XSide}` | `${XSide}-${YSide}`; -export type Coordinates = { x: number; y: number }; export type Width = "trigger" | string; export type Options = { diff --git a/packages/utilities/src/flyout/utilities/getRectFromCoordinates.ts b/packages/utilities/src/flyout/utilities/getRectFromCoordinates.ts index 03c06e0c..f33578ad 100644 --- a/packages/utilities/src/flyout/utilities/getRectFromCoordinates.ts +++ b/packages/utilities/src/flyout/utilities/getRectFromCoordinates.ts @@ -1,4 +1,4 @@ -import type { Coordinates } from "../types"; +import type { Coordinates } from "@/types/global"; /** * Include any missing properties to match DOMRect interface when coordinates are passed diff --git a/packages/utilities/src/flyout/utilities/tests/isFullyVisible.test.ts b/packages/utilities/src/flyout/utilities/tests/isFullyVisible.test.ts index 5392394b..1d511eb6 100644 --- a/packages/utilities/src/flyout/utilities/tests/isFullyVisible.test.ts +++ b/packages/utilities/src/flyout/utilities/tests/isFullyVisible.test.ts @@ -1,6 +1,7 @@ -import isFullyVisible from "flyout/utilities/isFullyVisible"; import { expect, test, describe } from "vitest"; +import isFullyVisible from "../isFullyVisible"; + describe("flyout/isFullyVisible", () => { test("returns true when flyout is fully visible within visual container", () => { const result = isFullyVisible({ diff --git a/packages/utilities/src/internal.ts b/packages/utilities/src/internal.ts index af0cb93c..b2c259cd 100644 --- a/packages/utilities/src/internal.ts +++ b/packages/utilities/src/internal.ts @@ -24,3 +24,5 @@ export { disableScroll, enableScroll } from "./scroll"; export { rafThrottle } from "./helpers"; export { getShadowRoot, findParent } from "./dom"; + +export type { Coordinates } from "./types/global"; diff --git a/packages/utilities/src/types/global.ts b/packages/utilities/src/types/global.ts new file mode 100644 index 00000000..20bd7d07 --- /dev/null +++ b/packages/utilities/src/types/global.ts @@ -0,0 +1 @@ +export type Coordinates = { x: number; y: number }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc52af2a..d4ddce5e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -186,7 +186,7 @@ importers: specifier: 2.0.2 version: 2.0.2(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@4.0.16) - packages/core: + packages/headless: dependencies: '@reshaped/utilities': specifier: workspace:* @@ -203,9 +203,9 @@ importers: '@csstools/postcss-global-data': specifier: 3.1.0 version: 3.1.0(postcss@8.5.6) - '@reshaped/utilities': + '@reshaped/headless': specifier: workspace:* - version: link:../utilities + version: link:../headless chalk: specifier: 4.1.2 version: 4.1.2