diff --git a/packages/react-table-devtools/package.json b/packages/react-table-devtools/package.json deleted file mode 100644 index 0aeb9821cd..0000000000 --- a/packages/react-table-devtools/package.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "name": "@tanstack/react-table-devtools", - "version": "9.0.0-alpha.10", - "description": "Devtools for React Table", - "author": "Tanner Linsley", - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/TanStack/table.git", - "directory": "packages/react-table-devtools" - }, - "homepage": "https://tanstack.com/table", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "keywords": [ - "react", - "table", - "react-table", - "datagrid" - ], - "type": "module", - "types": "dist/esm/index.d.ts", - "main": "dist/cjs/index.cjs", - "module": "dist/esm/index.js", - "exports": { - ".": { - "import": { - "types": "./dist/esm/index.d.ts", - "default": "./dist/esm/index.js" - }, - "require": { - "types": "./dist/cjs/index.d.cts", - "default": "./dist/cjs/index.cjs" - } - }, - "./package.json": "./package.json" - }, - "sideEffects": false, - "engines": { - "node": ">=12" - }, - "files": [ - "dist", - "src" - ], - "scripts": { - "clean": "rimraf ./build && rimraf ./dist", - "test:types": "tsc", - "test:build": "publint --strict", - "build": "vite build" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - }, - "devDependencies": { - "@types/react": "^19.0.1", - "@vitejs/plugin-react": "^4.3.4", - "react": "^19.0.0" - }, - "dependencies": { - "@tanstack/react-table": "workspace:*" - } -} diff --git a/packages/react-table-devtools/src/Explorer.tsx b/packages/react-table-devtools/src/Explorer.tsx deleted file mode 100644 index 621ec98362..0000000000 --- a/packages/react-table-devtools/src/Explorer.tsx +++ /dev/null @@ -1,230 +0,0 @@ -// @ts-nocheck - -import React from 'react' -import { styled } from './utils' - -const Entry = styled('div', { - fontFamily: 'Menlo, monospace', - fontSize: '0.7rem', - lineHeight: '1.7', - outline: 'none', - wordBreak: 'break-word', -}) - -const Label = styled('span', { - cursor: 'pointer', - color: 'white', -}) - -const Value = styled('span', (props, theme) => ({ - color: theme.danger, -})) - -const SubEntries = styled('div', { - marginLeft: '.1rem', - paddingLeft: '1rem', - borderLeft: '2px solid rgba(0,0,0,.15)', -}) - -const Info = styled('span', { - color: 'grey', - fontSize: '.7rem', -}) - -const Expander = ({ expanded, style = {}, ...rest }) => ( - - ▶ - -) - -const DefaultRenderer = ({ - handleEntry, - label, - value, - // path, - subEntries, - subEntryPages, - type, - // depth, - expanded, - toggle, - pageSize, - renderer, -}) => { - const [valueSnapshot, setValueSnapshot] = React.useState(undefined) - const [expandedPages, setExpandedPages] = React.useState([]) - - const refreshValueSnapshot = () => { - setValueSnapshot(value()) - } - - return ( - - {subEntryPages?.length ? ( - <> - toggle()}> - {label}{' '} - - {String(type).toLowerCase() === 'iterable' ? '(Iterable) ' : ''} - {subEntries.length} {subEntries.length > 1 ? `items` : `item`} - - - {expanded ? ( - subEntryPages.length === 1 ? ( - - {subEntries.map((entry) => handleEntry(entry))} - - ) : ( - - {subEntryPages.map((entries, index) => ( - - - - setExpandedPages((old) => - old.includes(index) - ? old.filter((d) => d !== index) - : [...old, index], - ) - } - > - [{index * pageSize} ...{' '} - {index * pageSize + pageSize - 1}] - - {expandedPages.includes(index) ? ( - - {entries.map((entry) => handleEntry(entry))} - - ) : null} - - - ))} - - ) - ) : null} - > - ) : type === 'function' ? ( - <> - - {label} 🔄{' '} - - } - value={valueSnapshot} - defaultExpanded={{}} - /> - > - ) : ( - <> - {label}:{' '} - - {JSON.stringify(value, Object.getOwnPropertyNames(Object(value)))} - - > - )} - - ) -} - -export default function Explorer({ - value, - defaultExpanded, - renderer = DefaultRenderer, - pageSize = 100, - depth = 0, - ...rest -}) { - const [expanded, setExpanded] = React.useState(defaultExpanded) - - const toggle = (set) => { - setExpanded((old) => (typeof set !== 'undefined' ? set : !old)) - } - - const path = [] - - let type = typeof value - let subEntries - const subEntryPages = [] - - const makeProperty = (sub) => { - const newPath = path.concat(sub.label) - const subDefaultExpanded = - defaultExpanded === true - ? { [sub.label]: true } - : defaultExpanded?.[sub.label] - return { - ...sub, - subPath: sub, - path: newPath, - depth: depth + 1, - defaultExpanded: subDefaultExpanded, - } - } - - if (Array.isArray(value)) { - type = 'array' - subEntries = value.map((d, i) => - makeProperty({ - label: i, - value: d, - }), - ) - } else if ( - value !== null && - typeof value === 'object' && - typeof value[Symbol.iterator] === 'function' - ) { - type = 'Iterable' - subEntries = Array.from(value, (val, i) => - makeProperty({ - label: i, - value: val, - }), - ) - } else if (typeof value === 'function') { - type = 'function' - } else if (typeof value === 'object' && value !== null) { - type = 'object' - // eslint-disable-next-line no-shadow - subEntries = Object.entries(value).map(([label, value]) => - makeProperty({ - label, - value, - }), - ) - } - - if (subEntries) { - let i = 0 - - while (i < subEntries.length) { - subEntryPages.push(subEntries.slice(i, i + pageSize)) - i = i + pageSize - } - } - - return renderer({ - handleEntry: (entry) => ( - - ), - type, - subEntries, - subEntryPages, - depth, - value, - path, - expanded, - toggle, - pageSize, - ...rest, - }) -} diff --git a/packages/react-table-devtools/src/Logo.tsx b/packages/react-table-devtools/src/Logo.tsx deleted file mode 100644 index ffe74d57eb..0000000000 --- a/packages/react-table-devtools/src/Logo.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import * as React from 'react' - -export default function Logo(props: any) { - return ( - - - - - - - - - - - - - - ) -} diff --git a/packages/react-table-devtools/src/index.tsx b/packages/react-table-devtools/src/index.tsx deleted file mode 100644 index 7d1ff8ad68..0000000000 --- a/packages/react-table-devtools/src/index.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import React from 'react' -import useLocalStorage from './useLocalStorage' -import { useIsMounted } from './utils' -import { Button, Panel } from './styledComponents' -import { ThemeProvider, defaultTheme as theme } from './theme' -// import { getQueryStatusLabel, getQueryStatusColor } from './utils' -import Explorer from './Explorer' -import Logo from './Logo' -import type { Table } from '@tanstack/react-table' - -interface DevtoolsOptions { - /** - * The react table to attach the devtools to. - */ - table: any - /** - * Set this true if you want the dev tools to default to being open - */ - initialIsOpen?: boolean - /** - * Use this to add props to the panel. For example, you can add className, style (merge and override default style), etc. - */ - panelProps?: React.DetailedHTMLProps< - React.HTMLAttributes, - HTMLDivElement - > - /** - * Use this to add props to the close button. For example, you can add className, style (merge and override default style), onClick (extend default handler), etc. - */ - closeButtonProps?: React.DetailedHTMLProps< - React.ButtonHTMLAttributes, - HTMLButtonElement - > - /** - * Use this to add props to the toggle button. For example, you can add className, style (merge and override default style), onClick (extend default handler), etc. - */ - toggleButtonProps?: React.DetailedHTMLProps< - React.ButtonHTMLAttributes, - HTMLButtonElement - > - /** - * Use this to render the devtools inside a different type of container element for a11y purposes. - * Any string which corresponds to a valid intrinsic JSX element is allowed. - * Defaults to 'footer'. - */ - containerElement?: string | any -} - -interface DevtoolsPanelOptions { - /** - * The react table to attach the devtools to. - */ - table: any - /** - * The standard React style object used to style a component with inline styles - */ - style?: React.CSSProperties - /** - * The standard React className property used to style a component with classes - */ - className?: string - /** - * A boolean variable indicating whether the panel is open or closed - */ - isOpen?: boolean - /** - * A function that toggles the open and close state of the panel - */ - setIsOpen: (isOpen: boolean) => void -} - -export function ReactTableDevtools({ - initialIsOpen, - table, - panelProps = {}, - toggleButtonProps = {}, - containerElement: Container = 'footer', -}: DevtoolsOptions): React.ReactElement | null { - const rootRef = React.useRef(null) - const panelRef = React.useRef(null) - const [isOpen, setIsOpen] = useLocalStorage( - 'reactTableDevtoolsOpen', - initialIsOpen, - ) - const isMounted = useIsMounted() - - const { style: panelStyle = {}, ...otherPanelProps } = panelProps - - const { - style: toggleButtonStyle = {}, - onClick: onToggleClick, - ...otherToggleButtonProps - } = toggleButtonProps - - // Do not render on the server - if (!isMounted()) return null - - return ( - - - {!isOpen ? ( - { - setIsOpen(true) - onToggleClick && onToggleClick(e) - }} - style={{ - background: 'none', - border: 0, - padding: 0, - margin: '.5rem', - display: 'inline-flex', - fontSize: '1.5em', - cursor: 'pointer', - width: 'fit-content', - ...toggleButtonStyle, - }} - > - - - ) : ( - - )} - - - ) -} - -export const ReactTableDevtoolsPanel = React.forwardRef< - HTMLDivElement, - DevtoolsPanelOptions ->(function ReactTableDevtoolsPanel(props, ref): React.ReactElement { - const { - table, - isOpen = true, - setIsOpen, - ...panelProps - } = props as DevtoolsPanelOptions & { - table: Table - } - - // const [activeMatchId, setActiveRouteId] = useLocalStorage( - // 'reactTableDevtoolsActiveRouteId', - // '', - // ) - - return ( - - - - - - setIsOpen(false)} - /> - - React Table{' '} - - Devtools - - - - {isOpen ? ( - { - setIsOpen(false) - }} - > - Close - - ) : null} - - - - - - - - - - - - - - - - - - {/* - - */} - - - - - ) -}) diff --git a/packages/react-table-devtools/src/styledComponents.ts b/packages/react-table-devtools/src/styledComponents.ts deleted file mode 100644 index 435d754392..0000000000 --- a/packages/react-table-devtools/src/styledComponents.ts +++ /dev/null @@ -1,106 +0,0 @@ -import { styled } from './utils' - -export const Panel = styled( - 'div', - (_props, theme) => ({ - fontSize: 'clamp(12px, 1.5vw, 14px)', - fontFamily: `sans-serif`, - display: 'flex', - backgroundColor: theme.background, - color: theme.foreground, - }), - { - '(max-width: 700px)': { - flexDirection: 'column', - }, - '(max-width: 600px)': { - fontSize: '.9em', - // flexDirection: 'column', - }, - }, -) - -const ActivePanel = styled( - 'div', - () => ({ - flex: '1 1 500px', - display: 'flex', - flexDirection: 'column', - overflow: 'auto', - height: '100%', - }), - { - '(max-width: 700px)': (_props, theme) => ({ - borderTop: `2px solid ${theme.gray}`, - }), - }, -) - -export const Button = styled('button', (props, theme) => ({ - appearance: 'none', - fontSize: '.9em', - fontWeight: 'bold', - background: theme.gray, - border: '0', - borderRadius: '.3em', - color: 'white', - padding: '.5em', - opacity: props.disabled ? '.5' : undefined, - cursor: 'pointer', -})) - -// export const QueryKeys = styled('span', { -// display: 'inline-block', -// fontSize: '0.9em', -// }) - -// export const QueryKey = styled('span', { -// display: 'inline-flex', -// alignItems: 'center', -// padding: '.2em .4em', -// fontWeight: 'bold', -// textShadow: '0 0 10px black', -// borderRadius: '.2em', -// }) - -const Code = styled('code', { - fontSize: '.9em', -}) - -const Input = styled('input', (_props, theme) => ({ - backgroundColor: theme.inputBackgroundColor, - border: 0, - borderRadius: '.2em', - color: theme.inputTextColor, - fontSize: '.9em', - lineHeight: `1.3`, - padding: '.3em .4em', -})) - -const Select = styled( - 'select', - (_props, theme) => ({ - display: `inline-block`, - fontSize: `.9em`, - fontFamily: `sans-serif`, - fontWeight: 'normal', - lineHeight: `1.3`, - padding: `.3em 1.5em .3em .5em`, - height: 'auto', - border: 0, - borderRadius: `.2em`, - appearance: `none`, - WebkitAppearance: 'none', - backgroundColor: theme.inputBackgroundColor, - backgroundImage: `url("data:image/svg+xml;utf8,")`, - backgroundRepeat: `no-repeat`, - backgroundPosition: `right .55em center`, - backgroundSize: `.65em auto, 100%`, - color: theme.inputTextColor, - }), - { - '(max-width: 500px)': { - display: 'none', - }, - }, -) diff --git a/packages/react-table-devtools/src/theme.tsx b/packages/react-table-devtools/src/theme.tsx deleted file mode 100644 index 371b353362..0000000000 --- a/packages/react-table-devtools/src/theme.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' - -export const defaultTheme = { - background: '#0b1521', - backgroundAlt: '#132337', - foreground: 'white', - gray: '#3f4e60', - grayAlt: '#222e3e', - inputBackgroundColor: '#fff', - inputTextColor: '#000', - success: '#00ab52', - danger: '#ff0085', - active: '#006bff', - warning: '#ffb200', -} as const - -export type Theme = typeof defaultTheme -interface ProviderProps { - theme: Theme - children?: React.ReactNode -} - -const ThemeContext = React.createContext(defaultTheme) - -export function ThemeProvider({ theme, ...rest }: ProviderProps) { - return -} - -export function useTheme() { - return React.useContext(ThemeContext) -} diff --git a/packages/react-table-devtools/src/useLocalStorage.ts b/packages/react-table-devtools/src/useLocalStorage.ts deleted file mode 100644 index f8174818f1..0000000000 --- a/packages/react-table-devtools/src/useLocalStorage.ts +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react' - -const getItem = (key: string): unknown => { - try { - const itemValue = localStorage.getItem(key) - if (typeof itemValue === 'string') { - return JSON.parse(itemValue) - } - return undefined - } catch { - return undefined - } -} - -export default function useLocalStorage( - key: string, - defaultValue: T | undefined, -): [T | undefined, (newVal: T | ((prevVal: T) => T)) => void] { - const [value, setValue] = React.useState() - - React.useEffect(() => { - const initialValue = getItem(key) as T | undefined - - if (typeof initialValue === 'undefined' || initialValue === null) { - setValue( - typeof defaultValue === 'function' ? defaultValue() : defaultValue, - ) - } else { - setValue(initialValue) - } - }, [defaultValue, key]) - - const setter = React.useCallback( - (updater: any) => { - setValue((old) => { - let newVal = updater - - if (typeof updater == 'function') { - newVal = updater(old) - } - try { - localStorage.setItem(key, JSON.stringify(newVal)) - } catch {} - - return newVal - }) - }, - [key], - ) - - return [value, setter] -} diff --git a/packages/react-table-devtools/src/useMediaQuery.ts b/packages/react-table-devtools/src/useMediaQuery.ts deleted file mode 100644 index d089d04620..0000000000 --- a/packages/react-table-devtools/src/useMediaQuery.ts +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -export default function useMediaQuery(query: string): boolean | undefined { - // Keep track of the preference in state, start with the current match - const [isMatch, setIsMatch] = React.useState(() => { - if (typeof window !== 'undefined') { - return window.matchMedia && window.matchMedia(query).matches - } - }) - - // Watch for changes - React.useEffect(() => { - if (typeof window !== 'undefined') { - if (!window.matchMedia) { - return - } - - // Create a matcher - const matcher = window.matchMedia(query) - - // Create our handler - const onChange = ({ matches }: { matches: boolean }) => - setIsMatch(matches) - - // Listen for changes - matcher.addListener(onChange) - - return () => { - // Stop listening for changes - matcher.removeListener(onChange) - } - } - }, [isMatch, query, setIsMatch]) - - return isMatch -} diff --git a/packages/react-table-devtools/src/utils.ts b/packages/react-table-devtools/src/utils.ts deleted file mode 100644 index a352712cfa..0000000000 --- a/packages/react-table-devtools/src/utils.ts +++ /dev/null @@ -1,136 +0,0 @@ -import React from 'react' -import { useTheme } from './theme' -import useMediaQuery from './useMediaQuery' -import type { Theme } from './theme' - -const isServer = typeof window === 'undefined' - -type StyledComponent = T extends 'button' - ? React.DetailedHTMLProps< - React.ButtonHTMLAttributes, - HTMLButtonElement - > - : T extends 'input' - ? React.DetailedHTMLProps< - React.InputHTMLAttributes, - HTMLInputElement - > - : T extends 'select' - ? React.DetailedHTMLProps< - React.SelectHTMLAttributes, - HTMLSelectElement - > - : T extends keyof HTMLElementTagNameMap - ? React.HTMLAttributes - : never - -// export function getStatusColor(match: RouteMatch, theme: Theme) { -// return match.isLoading -// ? theme.active -// : match.status === 'rejected' -// ? theme.danger -// : match.status === 'pending' -// ? theme.warning -// : theme.success -// } - -// export function getQueryStatusLabel(query: Query) { -// return query.state.isFetching -// ? 'fetching' -// : !query.getObserversCount() -// ? 'inactive' -// : query.isStale() -// ? 'stale' -// : 'fresh' -// } - -type Styles = - | React.CSSProperties - | ((props: Record, theme: Theme) => React.CSSProperties) - -export function styled( - type: T, - newStyles: Styles, - queries: Record = {}, -) { - return React.forwardRef>( - ({ style, ...rest }, ref) => { - const theme = useTheme() - - const mediaStyles = Object.entries(queries).reduce( - (current, [key, value]) => { - return useMediaQuery(key) - ? { - ...current, - ...(typeof value === 'function' ? value(rest, theme) : value), - } - : current - }, - {}, - ) - - return React.createElement(type, { - ...rest, - style: { - ...(typeof newStyles === 'function' - ? newStyles(rest, theme) - : newStyles), - ...style, - ...mediaStyles, - }, - ref, - }) - }, - ) -} - -export function useIsMounted() { - const mountedRef = React.useRef(false) - const isMounted = React.useCallback(() => mountedRef.current, []) - - React[isServer ? 'useEffect' : 'useLayoutEffect'](() => { - mountedRef.current = true - return () => { - mountedRef.current = false - } - }, []) - - return isMounted -} - -/** - * This hook is a safe useState version which schedules state updates in microtasks - * to prevent updating a component state while React is rendering different components - * or when the component is not mounted anymore. - */ -function useSafeState(initialState: T): [T, (value: T) => void] { - const isMounted = useIsMounted() - const [state, setState] = React.useState(initialState) - - const safeSetState = React.useCallback( - (value: T) => { - scheduleMicrotask(() => { - if (isMounted()) { - setState(value) - } - }) - }, - [isMounted], - ) - - return [state, safeSetState] -} - -/** - * Schedules a microtask. - * This can be useful to schedule state updates after rendering. - */ -function scheduleMicrotask(callback: () => void) { - Promise.resolve() - .then(callback) - .catch((error) => - setTimeout(() => { - throw error - }), - ) -} diff --git a/packages/react-table-devtools/tsconfig.json b/packages/react-table-devtools/tsconfig.json deleted file mode 100644 index 434a8d85fb..0000000000 --- a/packages/react-table-devtools/tsconfig.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "compilerOptions": { - "jsx": "react" - }, - "include": ["src", "vite.config.ts"] -} diff --git a/packages/react-table-devtools/vite.config.ts b/packages/react-table-devtools/vite.config.ts deleted file mode 100644 index 2573959ec6..0000000000 --- a/packages/react-table-devtools/vite.config.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { defineConfig, mergeConfig } from 'vitest/config' -import { tanstackViteConfig } from '@tanstack/config/vite' -import react from '@vitejs/plugin-react' - -const config = defineConfig({ - plugins: [react()], -}) - -export default mergeConfig( - config, - tanstackViteConfig({ - entry: './src/index.tsx', - srcDir: './src', - }), -) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfab0e5188..5c004160d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2790,25 +2790,6 @@ importers: specifier: ^19.0.0 version: 19.0.0 - packages/react-table-devtools: - dependencies: - '@tanstack/react-table': - specifier: workspace:* - version: link:../react-table - react-dom: - specifier: '>=16.8' - version: 18.3.1(react@19.0.0) - devDependencies: - '@types/react': - specifier: ^19.0.1 - version: 19.0.2 - '@vitejs/plugin-react': - specifier: ^4.3.4 - version: 4.3.4(vite@5.4.11(@types/node@22.10.2)(less@4.2.0)(sass@1.83.0)(terser@5.31.1)) - react: - specifier: ^19.0.0 - version: 19.0.0 - packages/solid-table: dependencies: '@tanstack/table-core': diff --git a/scripts/config.js b/scripts/config.js index 8d41706057..032a84ce29 100644 --- a/scripts/config.js +++ b/scripts/config.js @@ -40,10 +40,6 @@ export const packages = [ name: '@tanstack/vue-table', packageDir: 'packages/vue-table', }, - { - name: '@tanstack/react-table-devtools', - packageDir: 'packages/react-table-devtools', - }, { name: '@tanstack/match-sorter-utils', packageDir: 'packages/match-sorter-utils', diff --git a/vitest.workspace.js b/vitest.workspace.js index 4103534a26..d41630dbfb 100644 --- a/vitest.workspace.js +++ b/vitest.workspace.js @@ -1,14 +1,13 @@ import { defineWorkspace } from 'vitest/config' export default defineWorkspace([ - './packages/react-table-devtools/vite.config.ts', - './packages/svelte-table/vite.config.ts', - './packages/qwik-table/vite.config.ts', + './packages/angular-table/vite.config.ts', './packages/lit-table/vite.config.ts', - './packages/vue-table/vite.config.ts', - './packages/solid-table/vite.config.ts', + './packages/match-sorter-utils/vite.config.ts', + './packages/qwik-table/vite.config.ts', './packages/react-table/vite.config.ts', + './packages/solid-table/vite.config.ts', + './packages/svelte-table/vite.config.ts', './packages/table-core/vite.config.ts', - './packages/angular-table/vite.config.ts', - './packages/match-sorter-utils/vite.config.ts', + './packages/vue-table/vite.config.ts', ])