From e264333e48cd0ce858efebe1c32470d7b377ffd5 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Tue, 5 Dec 2023 10:18:31 +0000 Subject: [PATCH] Drop support for provider-less stores --- .../default/plate-ui/image-element.tsx | 12 ++++- .../default/plate-ui/media-embed-element.tsx | 13 ++++- .../src/registry/default/plate-ui/toolbar.tsx | 5 +- .../src/jotai-factory/createAtomProvider.tsx | 38 ++++++-------- .../jotai-factory/createAtomStore.spec.tsx | 52 ------------------- .../core/src/jotai-factory/createAtomStore.ts | 2 +- .../src/components/useResizableStore.ts | 13 ++--- packages/table/src/createTablePlugin.ts | 2 + packages/table/src/stores/tableStore.ts | 2 +- 9 files changed, 49 insertions(+), 90 deletions(-) diff --git a/apps/www/src/registry/default/plate-ui/image-element.tsx b/apps/www/src/registry/default/plate-ui/image-element.tsx index 4c9c294194..21c4ea0eeb 100644 --- a/apps/www/src/registry/default/plate-ui/image-element.tsx +++ b/apps/www/src/registry/default/plate-ui/image-element.tsx @@ -6,7 +6,7 @@ import { TImageElement, useMediaState, } from '@udecode/plate-media'; -import { useResizableStore } from '@udecode/plate-resizable'; +import { ResizableProvider, useResizableStore } from '@udecode/plate-resizable'; import { cn } from '@/lib/utils'; @@ -18,7 +18,7 @@ import { ResizeHandle, } from './resizable'; -export function ImageElement({ +function InnerImageElement({ className, children, nodeProps, @@ -70,3 +70,11 @@ export function ImageElement({ ); } + +export function ImageElement(props: PlateElementProps) { + return ( + + + + ); +} diff --git a/apps/www/src/registry/default/plate-ui/media-embed-element.tsx b/apps/www/src/registry/default/plate-ui/media-embed-element.tsx index 6584bb0c2b..897c8b6b8f 100644 --- a/apps/www/src/registry/default/plate-ui/media-embed-element.tsx +++ b/apps/www/src/registry/default/plate-ui/media-embed-element.tsx @@ -7,7 +7,7 @@ import { TMediaEmbedElement, useMediaState, } from '@udecode/plate-media'; -import { useResizableStore } from '@udecode/plate-resizable'; +import { ResizableProvider, useResizableStore } from '@udecode/plate-resizable'; import LiteYouTubeEmbed from 'react-lite-youtube-embed'; import { Tweet } from 'react-tweet'; @@ -21,7 +21,7 @@ import { ResizeHandle, } from './resizable'; -const MediaEmbedElement = React.forwardRef< +const InnerMediaEmbedElement = React.forwardRef< React.ElementRef, PlateElementProps >(({ className, children, ...props }, ref) => { @@ -136,6 +136,15 @@ const MediaEmbedElement = React.forwardRef< ); }); +InnerMediaEmbedElement.displayName = 'InnerMediaEmbedElement'; + +const MediaEmbedElement: typeof InnerMediaEmbedElement = React.forwardRef( + (props, ref) => ( + + + + ) +); MediaEmbedElement.displayName = 'MediaEmbedElement'; export { MediaEmbedElement }; diff --git a/apps/www/src/registry/default/plate-ui/toolbar.tsx b/apps/www/src/registry/default/plate-ui/toolbar.tsx index 7748b6e0c6..898e33d2a1 100644 --- a/apps/www/src/registry/default/plate-ui/toolbar.tsx +++ b/apps/www/src/registry/default/plate-ui/toolbar.tsx @@ -102,10 +102,7 @@ const ToolbarButton = React.forwardRef< const content = typeof pressed === 'boolean' ? ( - + { return `${storeName}:${scope}`; }; -// Map from store name to store. Used for provider-less stores only. -const globalAtomStores = new Map(); - /** - * Context mapping store name and scope to store. Used for providers. The - * 'provider' scope is used to reference any provider belonging to the store, - * regardless of scope. + * Context mapping store name and scope to store. The 'provider' scope is used + * to reference any provider belonging to the store, regardless of scope. */ const PROVIDER_SCOPE = 'provider'; const AtomStoreContext = createContext>(new Map()); -// Get the global store for the given store name, creating it if necessary. -const getGlobalAtomStore = (storeName: string): JotaiStore => { - if (!globalAtomStores.has(storeName)) { - globalAtomStores.set(storeName, createStore()); - } - - return globalAtomStores.get(storeName)!; -}; - /** * Tries to find a store in each of the following places, in order: * 1. The store context, matching the store name and scope * 2. The store context, matching the store name and 'provider' scope - * 3. The global store for the store name + * 3. Otherwise, return undefined */ export const useAtomStore = ( storeName: string, - scope: string = PROVIDER_SCOPE -): JotaiStore => { + scope: string = PROVIDER_SCOPE, + warnIfUndefined: boolean = true +): JotaiStore | undefined => { const storeContext = useContext(AtomStoreContext); - return ( + const store = storeContext.get(getFullyQualifiedScope(storeName, scope)) ?? - storeContext.get(getFullyQualifiedScope(storeName, PROVIDER_SCOPE)) ?? - getGlobalAtomStore(storeName) - ); + storeContext.get(getFullyQualifiedScope(storeName, PROVIDER_SCOPE)); + + if (!store && warnIfUndefined) { + console.warn( + `Tried to access jotai store '${storeName}' outside of a matching provider.` + ); + } + + return store; }; export type ProviderProps = AtomProviderProps & diff --git a/packages/core/src/jotai-factory/createAtomStore.spec.tsx b/packages/core/src/jotai-factory/createAtomStore.spec.tsx index 39eb202a7e..0a92a6eda2 100644 --- a/packages/core/src/jotai-factory/createAtomStore.spec.tsx +++ b/packages/core/src/jotai-factory/createAtomStore.spec.tsx @@ -153,42 +153,6 @@ describe('createAtomStore', () => { expect(getByText(INITIAL_NAME)).toBeInTheDocument(); expect(getByText(WRITE_ONLY_CONSUMER_AGE)).toBeInTheDocument(); }); - - it('works without a provider', () => { - const { getByText } = render( - <> - - - - ); - - expect(getByText(INITIAL_NAME)).toBeInTheDocument(); - expect(getByText(INITIAL_AGE)).toBeInTheDocument(); - - act(() => getByText('consumerSetAge').click()); - - expect(getByText(INITIAL_NAME)).toBeInTheDocument(); - expect(getByText(WRITE_ONLY_CONSUMER_AGE)).toBeInTheDocument(); - }); - - it('works adjacent to a provider', () => { - const { getByText } = render( - <> - - - - - - ); - - expect(getByText(INITIAL_NAME)).toBeInTheDocument(); - expect(getByText(INITIAL_AGE)).toBeInTheDocument(); - - act(() => getByText('consumerSetAge').click()); - - expect(getByText(INITIAL_NAME)).toBeInTheDocument(); - expect(getByText(INITIAL_AGE)).toBeInTheDocument(); - }); }); describe('scoped providers', () => { @@ -227,12 +191,6 @@ describe('createAtomStore', () => { ); }; - it('returns default value when no provider is present', () => { - const { getByText } = render(); - - expect(getByText('null')).toBeInTheDocument(); - }); - it('returns value of first ancestor when scope matches no provider', () => { const { getByText } = render( @@ -341,15 +299,5 @@ describe('createAtomStore', () => { expect(getByText('Jane')).toBeInTheDocument(); expect(getByText('98')).toBeInTheDocument(); }); - - it('works without provider', () => { - const { getByText } = render( - - - - ); - - expect(getByText('72')).toBeInTheDocument(); - }); }); }); diff --git a/packages/core/src/jotai-factory/createAtomStore.ts b/packages/core/src/jotai-factory/createAtomStore.ts index 5ed3c58424..2f44a2ccc3 100644 --- a/packages/core/src/jotai-factory/createAtomStore.ts +++ b/packages/core/src/jotai-factory/createAtomStore.ts @@ -169,7 +169,7 @@ export const createAtomStore = < atoms[key] = atomConfig; getAtoms[key] = (optionsOrScope: UseAtomOptionsOrScope = {}) => { const options = convertScopeShorthand(optionsOrScope); - const contextStore = useAtomStore(name, options.scope); + const contextStore = useAtomStore(name, options.scope, false); return useAtomValue(atomConfig, { store: options.store ?? contextStore, diff --git a/packages/resizable/src/components/useResizableStore.ts b/packages/resizable/src/components/useResizableStore.ts index 2ffcabf37c..eef4a4a2c3 100644 --- a/packages/resizable/src/components/useResizableStore.ts +++ b/packages/resizable/src/components/useResizableStore.ts @@ -1,9 +1,10 @@ import { CSSProperties } from 'react'; import { createAtomStore } from '@udecode/plate-common'; -export const { resizableStore, useResizableStore } = createAtomStore( - { - width: 0 as CSSProperties['width'], - }, - { name: 'resizable' } -); +export const { resizableStore, useResizableStore, ResizableProvider } = + createAtomStore( + { + width: 0 as CSSProperties['width'], + }, + { name: 'resizable' } + ); diff --git a/packages/table/src/createTablePlugin.ts b/packages/table/src/createTablePlugin.ts index 9e1384ba0e..bfbe9cbc94 100644 --- a/packages/table/src/createTablePlugin.ts +++ b/packages/table/src/createTablePlugin.ts @@ -1,6 +1,7 @@ import { createPluginFactory } from '@udecode/plate-common'; import { onKeyDownTable } from './onKeyDownTable'; +import { TableProvider } from './stores'; import { insertTableColumn, insertTableRow } from './transforms/index'; import { TablePlugin, TableStoreCellAttributes } from './types'; import { withTable } from './withTable'; @@ -40,6 +41,7 @@ export const createTablePlugin = createPluginFactory({ _cellIndices: new WeakMap() as TableStoreCellAttributes, }, withOverrides: withTable, + renderAboveEditable: TableProvider, plugins: [ { key: ELEMENT_TR, diff --git a/packages/table/src/stores/tableStore.ts b/packages/table/src/stores/tableStore.ts index b950c2519f..35894bdf24 100644 --- a/packages/table/src/stores/tableStore.ts +++ b/packages/table/src/stores/tableStore.ts @@ -3,7 +3,7 @@ import { createAtomStore, TElement } from '@udecode/plate-common'; export type TableStoreSizeOverrides = Map; -export const { tableStore, useTableStore } = createAtomStore( +export const { tableStore, useTableStore, TableProvider } = createAtomStore( { colSizeOverrides: new Map() as TableStoreSizeOverrides, rowSizeOverrides: new Map() as TableStoreSizeOverrides,