Skip to content

Commit e6a7673

Browse files
committed
chore: update provider
1 parent 1fcb66e commit e6a7673

File tree

10 files changed

+255
-45
lines changed

10 files changed

+255
-45
lines changed

src/components/RichTextEditor.tsx

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { Toaster } from '@/components/ui/toaster';
1111
import { EDITOR_UPDATE_WATCH_THROTTLE_WAIT_TIME } from '@/constants';
1212
import { RESET_CSS } from '@/constants/resetCSS';
1313
import { editableEditorActions } from '@/store/editableEditor';
14+
import { ProviderRichText } from '@/store/ProviderRichText';
1415
import { themeActions } from '@/theme/theme';
1516
import type { BubbleMenuProps, ToolbarProps } from '@/types';
1617
import { removeCSS, updateCSS } from '@/utils/dynamicCSS';
@@ -166,32 +167,33 @@ function RichTextEditor(props: RichTextEditorProps, ref: React.ForwardedRef<{ ed
166167

167168
return (
168169
<div className="reactjs-tiptap-editor">
169-
<TooltipProvider delayDuration={0}
170-
disableHoverableContent
171-
>
172-
<div className="richtext-overflow-hidden richtext-rounded-[0.5rem] richtext-bg-background richtext-shadow richtext-outline richtext-outline-1">
173-
174-
<div className="richtext-flex richtext-max-h-full richtext-w-full richtext-flex-col">
175-
{!props?.hideToolbar && <Toolbar disabled={!!props?.disabled}
176-
editor={editor}
177-
toolbar={props.toolbar}
178-
/>}
179-
180-
<EditorContent className={`richtext-relative ${props?.contentClass || ''}`}
181-
editor={editor}
182-
/>
183-
184-
{hasExtensionValue && <CharactorCount editor={editor}
185-
extensions={extensions}
186-
/>}
187-
188-
{!props?.hideBubble && <BubbleMenu bubbleMenu={props?.bubbleMenu}
189-
disabled={props?.disabled}
190-
editor={editor}
191-
/>}
170+
<ProviderRichText>
171+
<TooltipProvider delayDuration={0}
172+
disableHoverableContent
173+
>
174+
<div className="richtext-overflow-hidden richtext-rounded-[0.5rem] richtext-bg-background richtext-shadow richtext-outline richtext-outline-1">
175+
<div className="richtext-flex richtext-max-h-full richtext-w-full richtext-flex-col">
176+
{!props?.hideToolbar && <Toolbar disabled={!!props?.disabled}
177+
editor={editor}
178+
toolbar={props.toolbar}
179+
/>}
180+
181+
<EditorContent className={`richtext-relative ${props?.contentClass || ''}`}
182+
editor={editor}
183+
/>
184+
185+
{hasExtensionValue && <CharactorCount editor={editor}
186+
extensions={extensions}
187+
/>}
188+
189+
{!props?.hideBubble && <BubbleMenu bubbleMenu={props?.bubbleMenu}
190+
disabled={props?.disabled}
191+
editor={editor}
192+
/>}
193+
</div>
192194
</div>
193-
</div>
194-
</TooltipProvider>
195+
</TooltipProvider>
196+
</ProviderRichText>
195197

196198
<Toaster />
197199
</div>

src/extensions/Image/store.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { createSignal, useSignalValue } from 'reactjs-signal';
2-
3-
const dialogImage = createSignal(false);
1+
import { useStoreUploadImage } from '@/store/store';
2+
import { dispatchEvent } from '@/utils/customEvents/customEvents';
43

54
export function useDialogImage() {
6-
return useSignalValue(dialogImage);
5+
const [v] = useStoreUploadImage(store => store.value);
6+
7+
return v;
78
}
89

910
export const actionDialogImage = {
1011
setOpen: (value: boolean) => {
11-
dialogImage(value);
12+
dispatchEvent('UPLOAD_IMAGE', value);
1213
},
1314
};

src/extensions/Video/store.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { createSignal, useSignalValue } from 'reactjs-signal';
2-
3-
const dialogVideo = createSignal(false);
1+
import { useStoreUploadVideo } from '@/store/store';
2+
import { dispatchEvent } from '@/utils/customEvents/customEvents';
43

54
export function useDialogVideo() {
6-
return useSignalValue(dialogVideo);
5+
const [v] = useStoreUploadVideo(store => store.value);
6+
7+
return v;
78
}
89

910
export const actionDialogVideo = {
1011
setOpen: (value: boolean) => {
11-
dialogVideo(value);
12+
dispatchEvent('UPLOAD_VIDEO', value);
1213
},
1314
};

src/store/ProviderRichText.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/* eslint-disable react/display-name */
2+
/* eslint-disable @typescript-eslint/no-explicit-any */
3+
import React, { memo, useEffect } from 'react';
4+
5+
import { ProviderEditableEditor, ProviderTheme, ProviderUploadImage, ProviderUploadVideo, useStoreUploadImage, useStoreUploadVideo, useStoreEditableEditor, useStoreTheme } from '@/store/store';
6+
import { listenEvent } from '@/utils/customEvents/customEvents';
7+
8+
const EventInitial = memo(({ children }: any) => {
9+
const [, setUploadImage] = useStoreUploadImage(store => store.value);
10+
const [, setUploadVideo] = useStoreUploadVideo(store => store.value);
11+
const [, setEditable] = useStoreEditableEditor(store => store.value);
12+
const [, setTheme] = useStoreTheme(store => store.value);
13+
14+
const handleUploadImage = (evt: any) => {
15+
setUploadImage({
16+
value: evt.detail
17+
});
18+
};
19+
20+
const handleUploadVideo = (evt: any) => {
21+
setUploadVideo({
22+
value: evt.detail
23+
});
24+
};
25+
26+
const handleEditable = (evt: any) => {
27+
setEditable({
28+
value: evt.detail
29+
});
30+
};
31+
32+
const handleTheme = (evt: any) => {
33+
setTheme({
34+
value: evt.detail
35+
});
36+
};
37+
38+
useEffect(() => {
39+
const rm1 = listenEvent('UPLOAD_IMAGE', handleUploadImage);
40+
const rm2 = listenEvent('UPLOAD_VIDEO', handleUploadVideo);
41+
const rm3 = listenEvent('EDIT', handleEditable);
42+
const rm4 = listenEvent('UPDATE_THEME', handleTheme);
43+
44+
return () => {
45+
rm1();
46+
rm2();
47+
rm3();
48+
rm4();
49+
};
50+
}, []);
51+
52+
return (
53+
<>
54+
{children}
55+
</>
56+
);
57+
});
58+
59+
export function ProviderRichText ({ children }: { children: React.ReactNode }) {
60+
61+
return (
62+
<ProviderUploadImage>
63+
<ProviderUploadVideo>
64+
<ProviderEditableEditor>
65+
<ProviderTheme>
66+
<EventInitial>
67+
{children}
68+
</EventInitial>
69+
</ProviderTheme>
70+
</ProviderEditableEditor>
71+
</ProviderUploadVideo>
72+
</ProviderUploadImage>
73+
);
74+
}

src/store/editableEditor.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { createSignal, useSignalValue } from 'reactjs-signal';
2-
3-
const editableEditorProxy = createSignal(false);
1+
import { useStoreEditableEditor } from '@/store/store';
2+
import { dispatchEvent } from '@/utils/customEvents/customEvents';
43

54
export function useEditableEditor() {
6-
return useSignalValue(editableEditorProxy);
5+
const [v] = useStoreEditableEditor(store => store.value);
6+
7+
return v;
78
}
89

910
export const editableEditorActions = {
1011
setDisable: (disable: boolean) => {
11-
editableEditorProxy(disable);
12+
dispatchEvent('EDIT', disable);
1213
},
1314
};

src/store/fast-context.tsx

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import React, {
2+
useRef,
3+
createContext,
4+
useContext,
5+
useCallback,
6+
useSyncExternalStore,
7+
} from 'react';
8+
9+
export default function createFastContext<Store>(initialState: Store) {
10+
function useStoreData(): {
11+
get: () => Store;
12+
set: (value: Partial<Store>) => void;
13+
subscribe: (callback: () => void) => () => void; } {
14+
const store = useRef(initialState);
15+
16+
const get = useCallback(() => store.current, []);
17+
18+
const subscribers = useRef(new Set<() => void>());
19+
20+
const set = useCallback((value: Partial<Store>) => {
21+
store.current = { ...store.current, ...value };
22+
subscribers.current.forEach((callback) => callback());
23+
}, []);
24+
25+
const subscribe = useCallback((callback: () => void) => {
26+
subscribers.current.add(callback);
27+
return () => subscribers.current.delete(callback);
28+
}, []);
29+
30+
return {
31+
get,
32+
set,
33+
subscribe,
34+
};
35+
}
36+
37+
type UseStoreDataReturnType = ReturnType<typeof useStoreData>;
38+
39+
const StoreContext = createContext<UseStoreDataReturnType | null>(null);
40+
41+
function Provider({ children }: { children: React.ReactNode }) {
42+
return (
43+
<StoreContext.Provider value={useStoreData()}>
44+
{children}
45+
</StoreContext.Provider>
46+
);
47+
}
48+
49+
function useStore<SelectorOutput>(
50+
selector: (store: Store) => SelectorOutput
51+
): [SelectorOutput, (value: Partial<Store>) => void] {
52+
const store = useContext(StoreContext);
53+
if (!store) {
54+
throw new Error('Store not found');
55+
}
56+
57+
const state = useSyncExternalStore(
58+
store.subscribe,
59+
() => selector(store.get()),
60+
() => selector(initialState),
61+
);
62+
63+
return [state, store.set];
64+
}
65+
66+
return {
67+
Provider,
68+
useStore,
69+
};
70+
}

src/store/store.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import createFastContext from '@/store/fast-context';
2+
3+
const { Provider: ProviderUploadImage, useStore: useStoreUploadImage } = createFastContext({
4+
value: false
5+
});
6+
7+
const { Provider: ProviderUploadVideo, useStore: useStoreUploadVideo } = createFastContext({
8+
value: false
9+
});
10+
11+
const { Provider: ProviderEditableEditor, useStore: useStoreEditableEditor } = createFastContext({
12+
value: false
13+
});
14+
15+
const { Provider: ProviderTheme, useStore: useStoreTheme } = createFastContext({
16+
value: 'light'
17+
});
18+
19+
export {
20+
ProviderUploadImage,
21+
useStoreUploadImage,
22+
23+
ProviderUploadVideo,
24+
useStoreUploadVideo,
25+
26+
ProviderEditableEditor,
27+
useStoreEditableEditor,
28+
29+
ProviderTheme,
30+
useStoreTheme,
31+
};

src/theme/theme.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
import { createSignal, useSignalValue } from 'reactjs-signal';
2-
3-
const themeProxy = createSignal('light');
1+
import { useStoreTheme } from '@/store/store';
2+
import { dispatchEvent } from '@/utils/customEvents/customEvents';
43

54
export function useTheme() {
6-
return useSignalValue(themeProxy);
5+
const [v] = useStoreTheme(store => store.value);
6+
7+
return v;
78
}
89

910
export const themeActions = {
1011
setTheme: (theme: string) => {
11-
themeProxy(theme);
12+
dispatchEvent('UPDATE_THEME', theme);
1213
},
1314
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { type EventValues } from '@/utils/customEvents/events.constant';
2+
3+
export function listenEvent (eventName: EventValues, callback: any) {
4+
window.addEventListener(eventName, callback);
5+
6+
return () => {
7+
window.removeEventListener(eventName, callback);
8+
};
9+
}
10+
11+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
12+
export function dispatchEvent (eventName: EventValues, detail?: any) {
13+
window.dispatchEvent(
14+
new CustomEvent(eventName, {
15+
detail,
16+
}),
17+
);
18+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const EVENTS = {
2+
UPLOAD_IMAGE: 'UPLOAD_IMAGE',
3+
UPLOAD_VIDEO: 'UPLOAD_VIDEO',
4+
EDIT: 'EDIT',
5+
UPDATE_THEME: 'UPDATE_THEME',
6+
7+
UPDATE_LANG: 'UPDATE_LANG',
8+
} as const;
9+
10+
type EventsType = typeof EVENTS;
11+
export type EventValues = EventsType[keyof EventsType];

0 commit comments

Comments
 (0)