overkit 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +314 -0
- package/dist/index.d.mts +110 -0
- package/dist/index.mjs +305 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +75 -0
package/README.md
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# Overkit
|
|
2
|
+
|
|
3
|
+
Simplified overlay management system for React and Next.js. Uses Zustand for state management and allows creating modals, drawers, sheets, and more with ease.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Centralized State**: State management with Zustand
|
|
8
|
+
- **Simple Triggers**: Open overlays with a single click
|
|
9
|
+
- **Portals**: Flexible rendering with tunnel-rat
|
|
10
|
+
- **TypeScript**: Complete and safe typing
|
|
11
|
+
- **Dynamic Configuration**: Props based on store state
|
|
12
|
+
- **Built-in Hooks**: Access state from any component
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install overkit
|
|
18
|
+
# or
|
|
19
|
+
yarn add overkit
|
|
20
|
+
# or
|
|
21
|
+
pnpm add overkit
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Dependencies
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install zustand @radix-ui/react-slot tunnel-rat
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Basic Usage
|
|
31
|
+
|
|
32
|
+
### 1. Create a Registry
|
|
33
|
+
|
|
34
|
+
First, create a base component that will serve as the overlay (Modal, Drawer, Sheet, etc.):
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
// components/modal.tsx
|
|
38
|
+
import { registry, type RegistryComponentProps } from "overkit";
|
|
39
|
+
|
|
40
|
+
const Modal = ({
|
|
41
|
+
open,
|
|
42
|
+
onOpenChange,
|
|
43
|
+
title,
|
|
44
|
+
description,
|
|
45
|
+
children,
|
|
46
|
+
}: RegistryComponentProps) => {
|
|
47
|
+
if (!open) return null;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div className="modal-backdrop" onClick={() => onOpenChange?.(false)}>
|
|
51
|
+
<div className="modal-content">
|
|
52
|
+
<h2>{title}</h2>
|
|
53
|
+
<p>{description}</p>
|
|
54
|
+
{children}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const ModalRegistry = registry({
|
|
61
|
+
name: "modal",
|
|
62
|
+
render: Modal,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### 2. Configure Overkit
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
// overlays.tsx
|
|
70
|
+
import { Overkit } from "overkit";
|
|
71
|
+
import { ModalRegistry } from "./modal";
|
|
72
|
+
|
|
73
|
+
const o = new Overkit(["userModal", "confirmDialog"] as const)
|
|
74
|
+
.with(ModalRegistry)
|
|
75
|
+
.build();
|
|
76
|
+
|
|
77
|
+
// Create simple overlay
|
|
78
|
+
const userDialog = o.create("userModal", "modal").configure({
|
|
79
|
+
title: "User Profile",
|
|
80
|
+
description: "Manage your profile information",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
// Create overlay with extended state
|
|
84
|
+
const confirmDialog = o
|
|
85
|
+
.create("confirmDialog", "modal")
|
|
86
|
+
.extend<{ message: string }>(() => ({
|
|
87
|
+
message: "",
|
|
88
|
+
}))
|
|
89
|
+
.configure({
|
|
90
|
+
title: "Confirm Action",
|
|
91
|
+
description: "Are you sure?",
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Export components
|
|
95
|
+
export const UserModalTrigger = userDialog.trigger;
|
|
96
|
+
export const UserModalView = userDialog.view;
|
|
97
|
+
|
|
98
|
+
export const ConfirmTrigger = confirmDialog.trigger;
|
|
99
|
+
export const ConfirmView = confirmDialog.view;
|
|
100
|
+
export const useOverkitStore = o.useOverkitStore;
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 3. Use in Your App
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
// page.tsx
|
|
107
|
+
import { UserModalTrigger, UserModalView } from "./overlays";
|
|
108
|
+
|
|
109
|
+
export default function Page() {
|
|
110
|
+
return (
|
|
111
|
+
<div>
|
|
112
|
+
<UserModalTrigger>
|
|
113
|
+
<button>Open User Modal</button>
|
|
114
|
+
</UserModalTrigger>
|
|
115
|
+
|
|
116
|
+
<UserModalView />
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## API
|
|
123
|
+
|
|
124
|
+
### `Overkit`
|
|
125
|
+
|
|
126
|
+
The main class for creating and managing overlays.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
const o = new Overkit(["key1", "key2"] as const).with(RegistryItem).build();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### `.create(key, registryName)`
|
|
133
|
+
|
|
134
|
+
Creates a new overlay.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
const dialog = o.create("myDialog", "modal");
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `.extend<State>(storeCreator)`
|
|
141
|
+
|
|
142
|
+
Extends the overlay state with additional properties.
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
const dialog = o
|
|
146
|
+
.create("myDialog", "modal")
|
|
147
|
+
.extend<{ count: number }>((set) => ({
|
|
148
|
+
count: 0,
|
|
149
|
+
increment: () => set((state) => ({ count: state.count + 1 })),
|
|
150
|
+
}));
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `.configure(options)`
|
|
154
|
+
|
|
155
|
+
Configures the overlay properties. You can use static values or functions that receive the store:
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
const dialog = o
|
|
159
|
+
.create("productDialog", "modal")
|
|
160
|
+
.extend<{ mode: "create" | "edit" }>((set) => ({
|
|
161
|
+
mode: "create",
|
|
162
|
+
setMode: (mode) => set({ mode }),
|
|
163
|
+
}))
|
|
164
|
+
.configure({
|
|
165
|
+
// Static values
|
|
166
|
+
title: "Product",
|
|
167
|
+
|
|
168
|
+
// Functions with store access
|
|
169
|
+
title: (store) =>
|
|
170
|
+
store?.mode === "create" ? "Create Product" : "Edit Product",
|
|
171
|
+
description: (store) =>
|
|
172
|
+
store?.mode === "create"
|
|
173
|
+
? "Create a new product"
|
|
174
|
+
: "Edit existing product",
|
|
175
|
+
className: (store) =>
|
|
176
|
+
store?.mode === "create" ? "mode-create" : "mode-edit",
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `trigger`
|
|
181
|
+
|
|
182
|
+
Component to open the overlay.
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
<Trigger>
|
|
186
|
+
<button>Open</button>
|
|
187
|
+
</Trigger>
|
|
188
|
+
|
|
189
|
+
// With initial store values
|
|
190
|
+
<Trigger count={100}>
|
|
191
|
+
<button>Open with 100</button>
|
|
192
|
+
</Trigger>
|
|
193
|
+
|
|
194
|
+
// With componentProps for the view
|
|
195
|
+
<Trigger componentProps={{ items: ["a", "b", "c"] }}>
|
|
196
|
+
<button>Open with Items</button>
|
|
197
|
+
</Trigger>
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### `view`
|
|
201
|
+
|
|
202
|
+
Component that renders the overlay content.
|
|
203
|
+
|
|
204
|
+
```tsx
|
|
205
|
+
// Basic
|
|
206
|
+
const View = dialog.view(() => <div>Content</div>);
|
|
207
|
+
|
|
208
|
+
// With props
|
|
209
|
+
const View = dialog.view<{ items: string[] }>(({ items }) => (
|
|
210
|
+
<ul>
|
|
211
|
+
{items.map((item) => (
|
|
212
|
+
<li key={item}>{item}</li>
|
|
213
|
+
))}
|
|
214
|
+
</ul>
|
|
215
|
+
));
|
|
216
|
+
|
|
217
|
+
// With close function
|
|
218
|
+
const View = dialog.view(({ close }) => (
|
|
219
|
+
<div>
|
|
220
|
+
<button onClick={close}>Close</button>
|
|
221
|
+
</div>
|
|
222
|
+
));
|
|
223
|
+
|
|
224
|
+
// With useInnerContext (requires .extend())
|
|
225
|
+
const View = dialog.view(({ useInnerContext }) => {
|
|
226
|
+
const count = useInnerContext((state) => state.count);
|
|
227
|
+
const increment = useInnerContext((state) => state.increment);
|
|
228
|
+
|
|
229
|
+
return (
|
|
230
|
+
<div>
|
|
231
|
+
<p>Count: {count}</p>
|
|
232
|
+
<button onClick={increment}>Increment</button>
|
|
233
|
+
</div>
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### `useOverkitStore`
|
|
239
|
+
|
|
240
|
+
Hook to access the global state of all overlays.
|
|
241
|
+
|
|
242
|
+
```tsx
|
|
243
|
+
const isOpen = useOverkitStore((state) => state.states.myDialog);
|
|
244
|
+
const setOpen = useOverkitStore((state) => state.setMyDialog);
|
|
245
|
+
|
|
246
|
+
// Open/close programmatically
|
|
247
|
+
setOpen(true);
|
|
248
|
+
setOpen(false);
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Advanced Examples
|
|
252
|
+
|
|
253
|
+
### Product Sheet (Create/Edit)
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
const productSheet = o
|
|
257
|
+
.create("productSheet", "Sheet")
|
|
258
|
+
.extend<ProductState>((set) => ({
|
|
259
|
+
mode: "create",
|
|
260
|
+
setMode: (mode) => set({ mode }),
|
|
261
|
+
}))
|
|
262
|
+
.configure({
|
|
263
|
+
title: (store) => {
|
|
264
|
+
return store?.mode === "create" ? "Create a Product" : "Edit Product";
|
|
265
|
+
},
|
|
266
|
+
description: (store) => {
|
|
267
|
+
return store?.mode === "create"
|
|
268
|
+
? "Fill in the details of the product you want to create"
|
|
269
|
+
: "Fill in the details of the product you want to edit";
|
|
270
|
+
},
|
|
271
|
+
className: "!max-w-none w-3/8",
|
|
272
|
+
});
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Counter with Extended State
|
|
276
|
+
|
|
277
|
+
```tsx
|
|
278
|
+
const counterDialog = o
|
|
279
|
+
.create("counter", "modal")
|
|
280
|
+
.extend<{ count: number }>((set) => ({
|
|
281
|
+
count: 0,
|
|
282
|
+
}))
|
|
283
|
+
.configure({
|
|
284
|
+
title: "Counter Dialog",
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Open with initial value
|
|
288
|
+
<counterDialog.trigger count={100}>
|
|
289
|
+
<button>Open with 100</button>
|
|
290
|
+
</counterDialog.trigger>;
|
|
291
|
+
|
|
292
|
+
// Use in the view
|
|
293
|
+
const CounterView = counterDialog.view(({ useInnerContext }) => {
|
|
294
|
+
const count = useInnerContext((state) => state.count);
|
|
295
|
+
return <div>Count: {count}</div>;
|
|
296
|
+
});
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## TypeScript
|
|
300
|
+
|
|
301
|
+
Overkit is fully typed. When creating overlays, types are automatically inferred:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
// Keys are validated at compile time
|
|
305
|
+
const o = new Overkit(["dialog1", "dialog2"] as const);
|
|
306
|
+
|
|
307
|
+
// TypeScript knows only "dialog1" and "dialog2" are valid
|
|
308
|
+
o.create("dialog1", "modal"); // ✅
|
|
309
|
+
o.create("dialog3", "modal"); // ❌ Type error
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
## License
|
|
313
|
+
|
|
314
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import tunnel from "tunnel-rat";
|
|
3
|
+
import * as zustand from "zustand";
|
|
4
|
+
import { StoreApi, UseBoundStore } from "zustand";
|
|
5
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region src/types.d.ts
|
|
8
|
+
type Capitalize<S extends string> = S extends `${infer F}${infer R}` ? `${Uppercase<F>}${R}` : S;
|
|
9
|
+
type StoreStates<TKeys extends readonly string[]> = { [K in TKeys[number]]: boolean };
|
|
10
|
+
type StoreSetters<TKeys extends readonly string[]> = { [K in TKeys[number] as `set${Capitalize<K>}`]: (value: boolean) => void };
|
|
11
|
+
type FactoryStore<TKeys extends readonly string[]> = {
|
|
12
|
+
states: StoreStates<TKeys>;
|
|
13
|
+
} & StoreSetters<TKeys>;
|
|
14
|
+
type ValueOrFunction<T, Store> = T | ((value: Store) => T);
|
|
15
|
+
type _SharedProps<ExtendedProps extends object = object> = ExtendedProps & {
|
|
16
|
+
title: string;
|
|
17
|
+
description: string;
|
|
18
|
+
className: string;
|
|
19
|
+
beforeOpen?: () => void;
|
|
20
|
+
beforeClose?: () => void;
|
|
21
|
+
};
|
|
22
|
+
type RegistryHooks<Store = unknown> = {
|
|
23
|
+
beforeOpen?: (store?: Store, api?: StoreApi<Store>) => void;
|
|
24
|
+
beforeClose?: (store?: Store, api?: StoreApi<Store>) => void;
|
|
25
|
+
};
|
|
26
|
+
type InitViewProps<ExtendedProps extends object = object, Store = unknown> = ExtendedProps & { [K in keyof Omit<_SharedProps, keyof RegistryHooks>]?: ValueOrFunction<_SharedProps[K], Store> } & RegistryHooks<Store>;
|
|
27
|
+
type RegistryComponentProps<ExtendedProps extends object = object> = {
|
|
28
|
+
children?: React.ReactNode;
|
|
29
|
+
open?: boolean;
|
|
30
|
+
onOpenChange?: (open: boolean) => void;
|
|
31
|
+
t: ReturnType<typeof tunnel>;
|
|
32
|
+
} & _SharedProps<ExtendedProps>;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/core/registry.d.ts
|
|
35
|
+
interface RegistryItem<Key extends string = string, OtherProps extends object = object> {
|
|
36
|
+
name: Key;
|
|
37
|
+
render: (opt: RegistryComponentProps & InitViewProps<OtherProps>) => React.ReactElement | null;
|
|
38
|
+
}
|
|
39
|
+
declare const registry: <Key extends string = string, ItemProps extends object = object>(config: RegistryItem<Key, ItemProps>) => RegistryItem<Key, ItemProps>;
|
|
40
|
+
type RegistryItemMap = Record<string, RegistryItem<any, any>>;
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/core/builder.d.ts
|
|
43
|
+
type ViewProps<TProps extends object> = TProps & {
|
|
44
|
+
close: () => void;
|
|
45
|
+
FooterButtons: ReturnType<typeof tunnel>["In"];
|
|
46
|
+
};
|
|
47
|
+
type ExtendedState<T> = T & {
|
|
48
|
+
_internal?: any;
|
|
49
|
+
_injectedProps?: Record<string, any>;
|
|
50
|
+
};
|
|
51
|
+
type UseInnerContextHook<TState> = <TSelected = TState>(selector?: (state: TState) => TSelected) => TSelected;
|
|
52
|
+
type ValueProperties<T> = { [K in keyof T as T[K] extends ((...args: any[]) => any) ? never : K]: T[K] };
|
|
53
|
+
declare class PortalBuilder<TKeys extends readonly string[], TKey extends TKeys[number], TExtendedProps extends object = object, TExtendedState extends object = object, Extended extends boolean = false> {
|
|
54
|
+
private key;
|
|
55
|
+
private t;
|
|
56
|
+
private options;
|
|
57
|
+
private RegistryComponent;
|
|
58
|
+
private innerStore?;
|
|
59
|
+
private useFactoryStore;
|
|
60
|
+
private extended;
|
|
61
|
+
constructor(key: TKey, useFactoryStore: UseBoundStore<StoreApi<FactoryStore<TKeys>>>, HubItemComponent: React.ComponentType<RegistryComponentProps>, options?: InitViewProps<TExtendedProps>, extended?: Extended);
|
|
62
|
+
configure(options: InitViewProps<TExtendedProps, Extended extends true ? TExtendedState : never>): PortalBuilder<TKeys, TKey, TExtendedProps, TExtendedState, Extended>;
|
|
63
|
+
extend<TNewState extends object>(storeCreator: (set: StoreApi<ExtendedState<TNewState>>["setState"], get: StoreApi<ExtendedState<TNewState>>["getState"]) => TNewState): PortalBuilder<TKeys, TKey, TExtendedProps, TNewState, true>;
|
|
64
|
+
private getInnerStore;
|
|
65
|
+
get useInnerContext(): Extended extends true ? UseInnerContextHook<TExtendedState> : never;
|
|
66
|
+
private parseOptions;
|
|
67
|
+
view: <TProps extends object = object>(Component: React.ComponentType<ViewProps<TProps & (Extended extends true ? {
|
|
68
|
+
useInnerContext: UseInnerContextHook<TExtendedState>;
|
|
69
|
+
} : object)>>) => React.FC<TProps>;
|
|
70
|
+
trigger: <THubComponentProps extends object = object, TProps extends object = object>({
|
|
71
|
+
before,
|
|
72
|
+
componentProps,
|
|
73
|
+
...props
|
|
74
|
+
}: TProps & {
|
|
75
|
+
before?: (innerStore?: TExtendedState) => void;
|
|
76
|
+
componentProps?: Partial<THubComponentProps>;
|
|
77
|
+
children?: React.ReactNode;
|
|
78
|
+
} & ValueProperties<TExtendedState>) => react_jsx_runtime0.JSX.Element;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/core/store.d.ts
|
|
82
|
+
declare class Store<TKeys extends readonly string[]> {
|
|
83
|
+
private factoryStore;
|
|
84
|
+
private FactoryContext;
|
|
85
|
+
private keys;
|
|
86
|
+
private withProvider?;
|
|
87
|
+
constructor(keys: TKeys, withProvider?: boolean);
|
|
88
|
+
private createFactoryStore;
|
|
89
|
+
createProvider: () => React.FC<{
|
|
90
|
+
children: React.ReactNode;
|
|
91
|
+
}>;
|
|
92
|
+
getStore: () => UseBoundStore<StoreApi<FactoryStore<TKeys>>>;
|
|
93
|
+
private getStoreHook;
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/core/overkit.d.ts
|
|
97
|
+
declare class Overkit<TKeys extends readonly string[], Items extends RegistryItemMap = Record<string, never>> extends Store<TKeys> {
|
|
98
|
+
private registry;
|
|
99
|
+
constructor(keys: TKeys);
|
|
100
|
+
private create;
|
|
101
|
+
private get useOverkitStore();
|
|
102
|
+
with<THubItemName extends string, ItemProps extends object = object>(item: RegistryItem<THubItemName, ItemProps>): Overkit<TKeys, Items & Record<THubItemName, RegistryItem<THubItemName, ItemProps>>>;
|
|
103
|
+
build(): {
|
|
104
|
+
create: <TKey extends TKeys[number], THubItemName extends keyof Items, ItemProps extends React.ComponentProps<Items[THubItemName]["render"]>>(key: TKey, hubItemName: THubItemName) => PortalBuilder<TKeys, TKey, Omit<ItemProps, "title" | "beforeOpen" | "beforeClose" | "description" | "className" | "children" | "open" | "onOpenChange" | "t">, object, false>;
|
|
105
|
+
useOverkitStore: zustand.UseBoundStore<zustand.StoreApi<FactoryStore<TKeys>>>;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { Overkit, type RegistryComponentProps, registry };
|
|
110
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import { createContext, useCallback, useContext, useMemo, useRef } from "react";
|
|
3
|
+
import tunnel from "tunnel-rat";
|
|
4
|
+
import { create, useStore } from "zustand";
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
|
|
7
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/typeof.js
|
|
8
|
+
function _typeof(o) {
|
|
9
|
+
"@babel/helpers - typeof";
|
|
10
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
11
|
+
return typeof o;
|
|
12
|
+
} : function(o) {
|
|
13
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
14
|
+
}, _typeof(o);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/toPrimitive.js
|
|
19
|
+
function toPrimitive(t, r) {
|
|
20
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
21
|
+
var e = t[Symbol.toPrimitive];
|
|
22
|
+
if (void 0 !== e) {
|
|
23
|
+
var i = e.call(t, r || "default");
|
|
24
|
+
if ("object" != _typeof(i)) return i;
|
|
25
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
26
|
+
}
|
|
27
|
+
return ("string" === r ? String : Number)(t);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/toPropertyKey.js
|
|
32
|
+
function toPropertyKey(t) {
|
|
33
|
+
var i = toPrimitive(t, "string");
|
|
34
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region \0@oxc-project+runtime@0.112.0/helpers/defineProperty.js
|
|
39
|
+
function _defineProperty(e, r, t) {
|
|
40
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
41
|
+
value: t,
|
|
42
|
+
enumerable: !0,
|
|
43
|
+
configurable: !0,
|
|
44
|
+
writable: !0
|
|
45
|
+
}) : e[r] = t, e;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/core/registry.ts
|
|
50
|
+
const registry = (config) => {
|
|
51
|
+
return {
|
|
52
|
+
name: config.name,
|
|
53
|
+
render: config.render
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
var Registry = class {
|
|
57
|
+
constructor() {
|
|
58
|
+
_defineProperty(this, "items", []);
|
|
59
|
+
}
|
|
60
|
+
add(config) {
|
|
61
|
+
this.items.push(config);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
get(key) {
|
|
65
|
+
const item = this.items.find((item) => item.name === key);
|
|
66
|
+
if (!item) throw new Error(`Item ${String(key)} not found`);
|
|
67
|
+
return item;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/core/builder.tsx
|
|
73
|
+
const HOOKS = ["beforeOpen", "beforeClose"];
|
|
74
|
+
var PortalBuilder = class PortalBuilder {
|
|
75
|
+
constructor(key, useFactoryStore, HubItemComponent, options = {}, extended = false) {
|
|
76
|
+
_defineProperty(this, "key", void 0);
|
|
77
|
+
_defineProperty(this, "t", void 0);
|
|
78
|
+
_defineProperty(this, "options", void 0);
|
|
79
|
+
_defineProperty(this, "RegistryComponent", void 0);
|
|
80
|
+
_defineProperty(this, "innerStore", void 0);
|
|
81
|
+
_defineProperty(this, "useFactoryStore", void 0);
|
|
82
|
+
_defineProperty(this, "extended", void 0);
|
|
83
|
+
_defineProperty(this, "getInnerStore", () => {
|
|
84
|
+
const innerStore = this.innerStore;
|
|
85
|
+
return () => innerStore;
|
|
86
|
+
});
|
|
87
|
+
_defineProperty(this, "view", (Component) => {
|
|
88
|
+
const key = this.key;
|
|
89
|
+
const t = this.t;
|
|
90
|
+
const RegistryComponent = this.RegistryComponent;
|
|
91
|
+
const useInnerContextHook = this.getInnerStore()();
|
|
92
|
+
const useFactoryStore = this.useFactoryStore;
|
|
93
|
+
return (props) => {
|
|
94
|
+
const isOpen = useFactoryStore((store) => store.states[key]);
|
|
95
|
+
const capitalize = key.charAt(0).toUpperCase() + key.slice(1);
|
|
96
|
+
const openChange = useFactoryStore((store) => store[`set${capitalize}`]);
|
|
97
|
+
const injectedProps = this.extended ? useInnerContextHook((state) => state._injectedProps) : void 0;
|
|
98
|
+
const options = this.parseOptions(this.options);
|
|
99
|
+
const innerStore = this.innerStore;
|
|
100
|
+
const close = useCallback(() => {
|
|
101
|
+
options?.beforeClose?.();
|
|
102
|
+
if (innerStore) innerStore.setState({ _injectedProps: void 0 });
|
|
103
|
+
openChange(false);
|
|
104
|
+
}, [
|
|
105
|
+
openChange,
|
|
106
|
+
options,
|
|
107
|
+
innerStore
|
|
108
|
+
]);
|
|
109
|
+
const componentProps = {
|
|
110
|
+
...injectedProps,
|
|
111
|
+
...props,
|
|
112
|
+
close,
|
|
113
|
+
FooterButtons: t.In
|
|
114
|
+
};
|
|
115
|
+
if (this.extended) componentProps.useInnerContext = useInnerContextHook;
|
|
116
|
+
return /* @__PURE__ */ jsx(RegistryComponent, {
|
|
117
|
+
open: isOpen,
|
|
118
|
+
onOpenChange: openChange,
|
|
119
|
+
t,
|
|
120
|
+
...options,
|
|
121
|
+
children: /* @__PURE__ */ jsx(Component, { ...componentProps })
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
_defineProperty(this, "trigger", ({ before, componentProps, ...props }) => {
|
|
126
|
+
const key = this.key;
|
|
127
|
+
const capitalize = key.charAt(0).toUpperCase() + key.slice(1);
|
|
128
|
+
const openChange = this.useFactoryStore((store) => store[`set${capitalize}`]);
|
|
129
|
+
const { safeProps, innerStoreValues } = useMemo(() => {
|
|
130
|
+
if (!this.innerStore) return {
|
|
131
|
+
safeProps: props,
|
|
132
|
+
innerStoreValues: void 0
|
|
133
|
+
};
|
|
134
|
+
const innerStoreDefaultValues = this.innerStore.getInitialState();
|
|
135
|
+
const storeKeys = Object.keys(innerStoreDefaultValues);
|
|
136
|
+
const safeProps = {};
|
|
137
|
+
const innerStoreValues = { ...innerStoreDefaultValues };
|
|
138
|
+
for (const key in props) if (Object.prototype.hasOwnProperty.call(props, key)) if (!storeKeys.includes(key)) safeProps[key] = props[key];
|
|
139
|
+
else innerStoreValues[key] = props[key];
|
|
140
|
+
return {
|
|
141
|
+
safeProps,
|
|
142
|
+
innerStoreValues
|
|
143
|
+
};
|
|
144
|
+
}, [props]);
|
|
145
|
+
const handleClick = () => {
|
|
146
|
+
const portalStore = this.innerStore;
|
|
147
|
+
const options = this.parseOptions(this.options);
|
|
148
|
+
const innerStore = portalStore?.getState();
|
|
149
|
+
options?.beforeOpen?.();
|
|
150
|
+
if (portalStore) {
|
|
151
|
+
const stateUpdate = {
|
|
152
|
+
...innerStoreValues,
|
|
153
|
+
_injectedProps: componentProps ?? void 0
|
|
154
|
+
};
|
|
155
|
+
portalStore.setState(stateUpdate);
|
|
156
|
+
}
|
|
157
|
+
before?.(innerStore);
|
|
158
|
+
openChange(true);
|
|
159
|
+
};
|
|
160
|
+
return /* @__PURE__ */ jsx(Slot, {
|
|
161
|
+
onClick: handleClick,
|
|
162
|
+
...safeProps
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
this.key = key;
|
|
166
|
+
this.t = tunnel();
|
|
167
|
+
this.options = options;
|
|
168
|
+
this.RegistryComponent = HubItemComponent;
|
|
169
|
+
this.useFactoryStore = useFactoryStore;
|
|
170
|
+
this.extended = extended;
|
|
171
|
+
}
|
|
172
|
+
configure(options) {
|
|
173
|
+
const newBuilder = new PortalBuilder(this.key, this.useFactoryStore, this.RegistryComponent, {
|
|
174
|
+
...this.options,
|
|
175
|
+
...options
|
|
176
|
+
}, this.extended);
|
|
177
|
+
newBuilder.t = this.t;
|
|
178
|
+
newBuilder.innerStore = this.innerStore;
|
|
179
|
+
return newBuilder;
|
|
180
|
+
}
|
|
181
|
+
extend(storeCreator) {
|
|
182
|
+
const newBuilder = new PortalBuilder(this.key, this.useFactoryStore, this.RegistryComponent, this.options, true);
|
|
183
|
+
newBuilder.t = this.t;
|
|
184
|
+
newBuilder.innerStore = create()((set, get) => ({ ...storeCreator(set, get) }));
|
|
185
|
+
return newBuilder;
|
|
186
|
+
}
|
|
187
|
+
get useInnerContext() {
|
|
188
|
+
return this.getInnerStore();
|
|
189
|
+
}
|
|
190
|
+
parseOptions(options) {
|
|
191
|
+
const innerStore = this.innerStore;
|
|
192
|
+
return Object.fromEntries(Object.entries(options).map(([key, value]) => {
|
|
193
|
+
const state = innerStore?.getState() || {};
|
|
194
|
+
if (typeof value === "function") {
|
|
195
|
+
if (HOOKS.includes(key)) {
|
|
196
|
+
const fn = (store, storeApi) => {
|
|
197
|
+
return () => value(store, storeApi);
|
|
198
|
+
};
|
|
199
|
+
return [key, fn(state, innerStore)];
|
|
200
|
+
}
|
|
201
|
+
return [key, value(state)];
|
|
202
|
+
}
|
|
203
|
+
return [key, value];
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
//#endregion
|
|
209
|
+
//#region src/core/store.tsx
|
|
210
|
+
var Store = class {
|
|
211
|
+
constructor(keys, withProvider = false) {
|
|
212
|
+
_defineProperty(this, "factoryStore", void 0);
|
|
213
|
+
_defineProperty(this, "FactoryContext", void 0);
|
|
214
|
+
_defineProperty(this, "keys", void 0);
|
|
215
|
+
_defineProperty(this, "withProvider", void 0);
|
|
216
|
+
_defineProperty(this, "createProvider", () => {
|
|
217
|
+
if (!this.FactoryContext) throw new Error("FactoryContext is not defined");
|
|
218
|
+
const createStore = this.createFactoryStore;
|
|
219
|
+
const Context = this.FactoryContext;
|
|
220
|
+
const Provider = ({ children }) => {
|
|
221
|
+
const storeRef = useRef(null);
|
|
222
|
+
if (!storeRef.current) storeRef.current = createStore();
|
|
223
|
+
const store = storeRef.current;
|
|
224
|
+
return /* @__PURE__ */ jsxs(Context.Provider, {
|
|
225
|
+
value: store,
|
|
226
|
+
children: [
|
|
227
|
+
" ",
|
|
228
|
+
children,
|
|
229
|
+
" "
|
|
230
|
+
]
|
|
231
|
+
});
|
|
232
|
+
};
|
|
233
|
+
return Provider;
|
|
234
|
+
});
|
|
235
|
+
_defineProperty(this, "getStore", () => {
|
|
236
|
+
if (this.withProvider) return this.getStoreHook();
|
|
237
|
+
return this.factoryStore;
|
|
238
|
+
});
|
|
239
|
+
_defineProperty(this, "getStoreHook", () => {
|
|
240
|
+
if (!this.FactoryContext) throw new Error("FactoryContext is not defined");
|
|
241
|
+
const FactoryContext = this.FactoryContext;
|
|
242
|
+
return function useStore_(selector) {
|
|
243
|
+
const store = useContext(FactoryContext);
|
|
244
|
+
if (!store) throw new Error("useStore must be used within a Provider");
|
|
245
|
+
return useStore(store, selector);
|
|
246
|
+
};
|
|
247
|
+
});
|
|
248
|
+
this.keys = keys;
|
|
249
|
+
this.withProvider = withProvider;
|
|
250
|
+
if (withProvider) this.FactoryContext = createContext(null);
|
|
251
|
+
else this.factoryStore = this.createFactoryStore();
|
|
252
|
+
}
|
|
253
|
+
createFactoryStore() {
|
|
254
|
+
return create()((set) => {
|
|
255
|
+
const states = {};
|
|
256
|
+
for (const key of this.keys) states[key] = false;
|
|
257
|
+
const setters = {};
|
|
258
|
+
for (const key of this.keys) {
|
|
259
|
+
const setterKey = `set${key.charAt(0).toUpperCase() + key.slice(1)}`;
|
|
260
|
+
setters[setterKey] = ((value) => set((state) => ({
|
|
261
|
+
...state,
|
|
262
|
+
states: {
|
|
263
|
+
...state.states,
|
|
264
|
+
[key]: value
|
|
265
|
+
}
|
|
266
|
+
})));
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
states,
|
|
270
|
+
...setters
|
|
271
|
+
};
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
//#endregion
|
|
277
|
+
//#region src/core/overkit.tsx
|
|
278
|
+
var Overkit = class extends Store {
|
|
279
|
+
constructor(keys) {
|
|
280
|
+
super(keys);
|
|
281
|
+
_defineProperty(this, "registry", void 0);
|
|
282
|
+
this.registry = new Registry();
|
|
283
|
+
}
|
|
284
|
+
create(key, hubItemName) {
|
|
285
|
+
const RegistryComponent = this.registry.get(hubItemName).render;
|
|
286
|
+
return new PortalBuilder(key, this.useOverkitStore, RegistryComponent);
|
|
287
|
+
}
|
|
288
|
+
get useOverkitStore() {
|
|
289
|
+
return this.getStore();
|
|
290
|
+
}
|
|
291
|
+
with(item) {
|
|
292
|
+
this.registry.add(item);
|
|
293
|
+
return this;
|
|
294
|
+
}
|
|
295
|
+
build() {
|
|
296
|
+
return {
|
|
297
|
+
create: this.create.bind(this),
|
|
298
|
+
useOverkitStore: this.useOverkitStore
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
//#endregion
|
|
304
|
+
export { Overkit, registry };
|
|
305
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/core/registry.ts","../src/core/builder.tsx","../src/core/store.tsx","../src/core/overkit.tsx"],"sourcesContent":["import type React from 'react';\r\nimport type { RegistryComponentProps, InitViewProps } from '../types';\r\nexport type { RegistryComponentProps }\r\n\r\n\r\nexport interface RegistryItem<\r\n Key extends string = string,\r\n OtherProps extends object = object\r\n> {\r\n name: Key;\r\n render: (\r\n opt: RegistryComponentProps & InitViewProps<OtherProps>\r\n ) => React.ReactElement | null\r\n}\r\n\r\nexport const registry = <\r\n Key extends string = string,\r\n ItemProps extends object = object\r\n>(\r\n config: RegistryItem<Key, ItemProps>\r\n): RegistryItem<Key, ItemProps> => {\r\n return {\r\n name: config.name,\r\n render: config.render,\r\n };\r\n};\r\n\r\n\r\nexport type RegistryItemMap = Record<string, RegistryItem<any, any>>;\r\n\r\nexport class Registry<\r\n Items extends RegistryItemMap = Record<string, never>\r\n> {\r\n private items: RegistryItem<string, object>[] = [];\r\n\r\n add<Key extends string, ItemProps extends object = object>(\r\n config: RegistryItem<Key, ItemProps>\r\n ): Registry<Items & Record<Key, RegistryItem<Key, ItemProps>>> {\r\n this.items.push(config as unknown as RegistryItem<string, object>);\r\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\r\n return this as any as Registry<\r\n Items & Record<Key, RegistryItem<Key, ItemProps>>\r\n >;\r\n }\r\n\r\n get<Key extends keyof Items>(key: Key): Items[Key] {\r\n const item = this.items.find((item) => item.name === key);\r\n if (!item) throw new Error(`Item ${String(key)} not found`);\r\n return item as Items[Key];\r\n }\r\n}\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\nimport { Slot } from \"@radix-ui/react-slot\";\r\nimport type React from \"react\";\r\nimport { useCallback, useMemo } from \"react\";\r\nimport tunnel from \"tunnel-rat\";\r\nimport { create, type StoreApi, type UseBoundStore } from \"zustand\";\r\n\r\nimport type {\r\n _SharedProps,\r\n Capitalize,\r\n FactoryStore,\r\n RegistryComponentProps,\r\n InitViewProps,\r\n} from \"../types\";\r\n\r\ntype ViewProps<TProps extends object> = TProps & {\r\n close: () => void;\r\n FooterButtons: ReturnType<typeof tunnel>[\"In\"];\r\n};\r\n\r\ntype ExtendedState<T> = T & {\r\n _internal?: any;\r\n\r\n _injectedProps?: Record<string, any>;\r\n};\r\n\r\ntype UseInnerContextHook<TState> = <TSelected = TState>(\r\n selector?: (state: TState) => TSelected,\r\n) => TSelected;\r\n\r\ntype ValueProperties<T> = {\r\n [K in keyof T as T[K] extends (...args: any[]) => any ? never : K]: T[K];\r\n};\r\n\r\nconst HOOKS = [\"beforeOpen\", \"beforeClose\"];\r\n\r\nexport class PortalBuilder<\r\n TKeys extends readonly string[],\r\n TKey extends TKeys[number],\r\n TExtendedProps extends object = object,\r\n TExtendedState extends object = object,\r\n Extended extends boolean = false,\r\n> {\r\n private key: TKey;\r\n private t: ReturnType<typeof tunnel>;\r\n private options: InitViewProps<TExtendedProps>;\r\n private RegistryComponent: React.ComponentType<RegistryComponentProps>;\r\n private innerStore?: UseBoundStore<StoreApi<ExtendedState<TExtendedState>>>;\r\n private useFactoryStore: UseBoundStore<StoreApi<FactoryStore<TKeys>>>;\r\n private extended: boolean;\r\n\r\n constructor(\r\n key: TKey,\r\n useFactoryStore: UseBoundStore<StoreApi<FactoryStore<TKeys>>>,\r\n HubItemComponent: React.ComponentType<RegistryComponentProps>,\r\n options: InitViewProps<TExtendedProps> = {} as InitViewProps<TExtendedProps>,\r\n extended: Extended = false as Extended,\r\n ) {\r\n this.key = key;\r\n this.t = tunnel();\r\n this.options = options;\r\n this.RegistryComponent = HubItemComponent;\r\n this.useFactoryStore = useFactoryStore;\r\n this.extended = extended;\r\n }\r\n\r\n configure(\r\n options: InitViewProps<TExtendedProps, Extended extends true ? TExtendedState : never>,\r\n ): PortalBuilder<TKeys, TKey, TExtendedProps, TExtendedState, Extended> {\r\n const newBuilder = new PortalBuilder<TKeys, TKey, TExtendedProps, TExtendedState, Extended>(\r\n this.key,\r\n this.useFactoryStore,\r\n this.RegistryComponent,\r\n { ...this.options, ...options },\r\n this.extended as Extended,\r\n );\r\n newBuilder.t = this.t;\r\n newBuilder.innerStore = this.innerStore;\r\n\r\n return newBuilder;\r\n }\r\n\r\n extend<TNewState extends object>(\r\n storeCreator: (\r\n set: StoreApi<ExtendedState<TNewState>>[\"setState\"],\r\n get: StoreApi<ExtendedState<TNewState>>[\"getState\"],\r\n ) => TNewState,\r\n ): PortalBuilder<TKeys, TKey, TExtendedProps, TNewState, true> {\r\n const newBuilder = new PortalBuilder<TKeys, TKey, TExtendedProps, TNewState, true>(\r\n this.key,\r\n this.useFactoryStore,\r\n this.RegistryComponent,\r\n this.options,\r\n true,\r\n );\r\n newBuilder.t = this.t;\r\n\r\n newBuilder.innerStore = create<ExtendedState<TNewState>>()((set, get) => ({\r\n ...storeCreator(set, get),\r\n }));\r\n\r\n // TODO: omit extended state from newBuilder\r\n return newBuilder;\r\n }\r\n\r\n private getInnerStore = () => {\r\n const innerStore = this.innerStore;\r\n\r\n return () => innerStore!;\r\n };\r\n\r\n get useInnerContext(): Extended extends true ? UseInnerContextHook<TExtendedState> : never {\r\n return this.getInnerStore() as Extended extends true\r\n ? UseInnerContextHook<TExtendedState>\r\n : never;\r\n }\r\n\r\n private parseOptions(options: InitViewProps<TExtendedProps>): _SharedProps<TExtendedProps> {\r\n const innerStore = this.innerStore;\r\n\r\n const parsedOptions = Object.fromEntries(\r\n Object.entries(options).map(([key, value]) => {\r\n const state = innerStore?.getState() || ({} as TExtendedState);\r\n if (typeof value === \"function\") {\r\n const isHook = HOOKS.includes(key);\r\n if (isHook) {\r\n const fn = (store?: TExtendedState, storeApi?: StoreApi<TExtendedState>) => {\r\n return () =>\r\n (value as (store?: TExtendedState, storeApi?: StoreApi<TExtendedState>) => void)(\r\n store,\r\n storeApi,\r\n );\r\n };\r\n return [key, fn(state, innerStore)];\r\n }\r\n const resolvedValue = value(state);\r\n return [key, resolvedValue];\r\n }\r\n return [key, value];\r\n }),\r\n ) as _SharedProps<TExtendedProps>;\r\n\r\n return parsedOptions;\r\n }\r\n\r\n view = <TProps extends object = object>(\r\n Component: React.ComponentType<\r\n ViewProps<\r\n TProps &\r\n (Extended extends true\r\n ? {\r\n useInnerContext: UseInnerContextHook<TExtendedState>;\r\n }\r\n : object)\r\n >\r\n >,\r\n ): React.FC<TProps> => {\r\n const key = this.key;\r\n const t = this.t;\r\n\r\n const RegistryComponent = this.RegistryComponent;\r\n const useInnerContextHook = this.getInnerStore()();\r\n const useFactoryStore = this.useFactoryStore;\r\n\r\n return (props: TProps) => {\r\n const isOpen = useFactoryStore((store) => store.states[key]);\r\n const capitalize = (key.charAt(0).toUpperCase() + key.slice(1)) as Capitalize<TKey>;\r\n const openChange = useFactoryStore(\r\n (store) => store[`set${capitalize}` as keyof typeof store] as (value: boolean) => void,\r\n );\r\n\r\n const injectedProps = this.extended\r\n ? useInnerContextHook((state) => state._injectedProps)\r\n : undefined;\r\n\r\n const options = this.parseOptions(this.options);\r\n\r\n const innerStore = this.innerStore;\r\n\r\n const close = useCallback(() => {\r\n options?.beforeClose?.();\r\n // Clear injected props when closing\r\n if (innerStore) {\r\n innerStore.setState({ _injectedProps: undefined } as Partial<\r\n ExtendedState<TExtendedState>\r\n >);\r\n }\r\n openChange(false);\r\n }, [openChange, options, innerStore]);\r\n\r\n const componentProps: any = {\r\n ...injectedProps,\r\n ...props,\r\n close,\r\n FooterButtons: t.In,\r\n };\r\n\r\n if (this.extended) {\r\n componentProps.useInnerContext = useInnerContextHook;\r\n }\r\n\r\n return (\r\n <RegistryComponent open={isOpen} onOpenChange={openChange} t={t} {...options}>\r\n <Component {...componentProps} />\r\n </RegistryComponent>\r\n );\r\n };\r\n };\r\n\r\n trigger = <THubComponentProps extends object = object, TProps extends object = object>({\r\n before,\r\n componentProps,\r\n ...props\r\n }: TProps & {\r\n before?: (innerStore?: TExtendedState) => void;\r\n componentProps?: Partial<THubComponentProps>;\r\n children?: React.ReactNode;\r\n } & ValueProperties<TExtendedState>) => {\r\n const key = this.key;\r\n const capitalize = (key.charAt(0).toUpperCase() + key.slice(1)) as Capitalize<TKey>;\r\n\r\n const openChange = this.useFactoryStore(\r\n (store) => store[`set${capitalize}` as keyof typeof store] as (value: boolean) => void,\r\n );\r\n\r\n // eslint-disable-next-line react-hooks/rules-of-hooks\r\n const { safeProps, innerStoreValues } = useMemo(() => {\r\n // return safeProps values that is included in this.innerStore.getInitialState()\r\n const portalStore = this.innerStore;\r\n\r\n if (!portalStore) return { safeProps: props, innerStoreValues: undefined };\r\n const innerStoreDefaultValues = this.innerStore!.getInitialState();\r\n const storeKeys = Object.keys(innerStoreDefaultValues);\r\n\r\n const safeProps: Record<string, unknown> = {};\r\n const innerStoreValues: Partial<TExtendedState> = {\r\n ...innerStoreDefaultValues,\r\n };\r\n\r\n for (const key in props) {\r\n if (Object.prototype.hasOwnProperty.call(props, key)) {\r\n if (!storeKeys.includes(key)) {\r\n safeProps[key] = (props as Record<string, unknown>)[key];\r\n } else {\r\n innerStoreValues[key as keyof TExtendedState] = (props as TExtendedState)[\r\n key as keyof TExtendedState\r\n ];\r\n }\r\n }\r\n }\r\n\r\n return { safeProps, innerStoreValues };\r\n }, [props]);\r\n\r\n const handleClick = () => {\r\n const portalStore = this.innerStore;\r\n const options = this.parseOptions(this.options);\r\n const innerStore = portalStore?.getState();\r\n\r\n options?.beforeOpen?.();\r\n\r\n if (portalStore) {\r\n const stateUpdate = {\r\n ...innerStoreValues,\r\n _injectedProps: (componentProps as any) ?? undefined,\r\n } as Partial<ExtendedState<TExtendedState>>;\r\n\r\n portalStore.setState(stateUpdate);\r\n }\r\n\r\n before?.(innerStore);\r\n openChange(true);\r\n };\r\n\r\n return <Slot onClick={handleClick} {...safeProps} />;\r\n };\r\n}\r\n","/* eslint-disable @typescript-eslint/no-explicit-any */\r\nimport type React from \"react\";\r\nimport { createContext, useContext, useRef } from \"react\";\r\n\r\nimport { create, type StoreApi, type UseBoundStore, useStore } from \"zustand\";\r\nimport type { FactoryStore, StoreSetters, StoreStates } from \"../types\";\r\n\r\nexport class Store<TKeys extends readonly string[]> {\r\n private factoryStore: UseBoundStore<StoreApi<FactoryStore<TKeys>>> | undefined;\r\n private FactoryContext: React.Context<UseBoundStore<StoreApi<FactoryStore<TKeys>>>> | undefined;\r\n private keys: TKeys;\r\n private withProvider?: boolean;\r\n constructor(keys: TKeys, withProvider = false) {\r\n this.keys = keys;\r\n this.withProvider = withProvider;\r\n\r\n if (withProvider) {\r\n this.FactoryContext = createContext<UseBoundStore<StoreApi<FactoryStore<TKeys>>>>(\r\n null as unknown as UseBoundStore<StoreApi<FactoryStore<TKeys>>>,\r\n );\r\n } else {\r\n this.factoryStore = this.createFactoryStore();\r\n }\r\n }\r\n\r\n private createFactoryStore() {\r\n return create<FactoryStore<TKeys>>()((set) => {\r\n const states = {} as StoreStates<TKeys>;\r\n for (const key of this.keys) {\r\n states[key as TKeys[number]] = false;\r\n }\r\n\r\n const setters = {} as StoreSetters<TKeys>;\r\n for (const key of this.keys) {\r\n const capitalizedKey = (key.charAt(0).toUpperCase() + key.slice(1)) as Capitalize<\r\n TKeys[number]\r\n >;\r\n const setterKey = `set${capitalizedKey}` as keyof StoreSetters<TKeys>;\r\n\r\n setters[setterKey] = ((value: boolean) =>\r\n set((state) => ({\r\n ...state,\r\n states: { ...state.states, [key]: value },\r\n }))) as any;\r\n }\r\n\r\n return {\r\n states,\r\n ...setters,\r\n };\r\n });\r\n }\r\n\r\n public createProvider = () => {\r\n if (!this.FactoryContext) {\r\n throw new Error(\"FactoryContext is not defined\");\r\n }\r\n\r\n const createStore = this.createFactoryStore;\r\n const Context = this.FactoryContext!;\r\n\r\n const Provider: React.FC<{ children: React.ReactNode }> = ({ children }) => {\r\n const storeRef = useRef<UseBoundStore<StoreApi<FactoryStore<TKeys>>> | null>(null);\r\n\r\n if (!storeRef.current) {\r\n storeRef.current = createStore();\r\n }\r\n\r\n const store = storeRef.current;\r\n\r\n return <Context.Provider value={store}> {children} </Context.Provider>;\r\n };\r\n\r\n return Provider;\r\n };\r\n\r\n public getStore = () => {\r\n if (this.withProvider) {\r\n return this.getStoreHook();\r\n }\r\n return this.factoryStore!;\r\n };\r\n\r\n private getStoreHook = () => {\r\n if (!this.FactoryContext) {\r\n throw new Error(\"FactoryContext is not defined\");\r\n }\r\n const FactoryContext = this.FactoryContext;\r\n\r\n return function useStore_<T>(selector: (state: FactoryStore<TKeys>) => T) {\r\n const store = useContext(FactoryContext);\r\n\r\n if (!store) {\r\n throw new Error(\"useStore must be used within a Provider\");\r\n }\r\n\r\n return useStore(store, selector);\r\n } as UseBoundStore<StoreApi<FactoryStore<TKeys>>>;\r\n };\r\n}\r\n","import type React from \"react\";\r\nimport { PortalBuilder } from \"./builder\";\r\nimport { Registry, type RegistryItem, type RegistryItemMap } from \"./registry\";\r\nimport { Store } from \"./store\";\r\nimport type { RegistryComponentProps } from \"../types\";\r\n\r\nexport class Overkit<\r\n TKeys extends readonly string[],\r\n Items extends RegistryItemMap = Record<string, never>,\r\n> extends Store<TKeys> {\r\n private registry: Registry<Items>;\r\n\r\n constructor(keys: TKeys) {\r\n super(keys);\r\n this.registry = new Registry<Items>();\r\n }\r\n\r\n private create<\r\n TKey extends TKeys[number],\r\n THubItemName extends keyof Items,\r\n ItemProps extends React.ComponentProps<Items[THubItemName][\"render\"]>,\r\n >(key: TKey, hubItemName: THubItemName) {\r\n const registryElement = this.registry.get(hubItemName);\r\n const RegistryComponent = registryElement.render;\r\n return new PortalBuilder<TKeys, TKey, Omit<ItemProps, keyof RegistryComponentProps>>(\r\n key,\r\n this.useOverkitStore,\r\n RegistryComponent,\r\n );\r\n }\r\n\r\n private get useOverkitStore() {\r\n return this.getStore();\r\n }\r\n\r\n with<THubItemName extends string, ItemProps extends object = object>(\r\n item: RegistryItem<THubItemName, ItemProps>,\r\n ): Overkit<TKeys, Items & Record<THubItemName, RegistryItem<THubItemName, ItemProps>>> {\r\n this.registry.add<THubItemName, ItemProps>(item);\r\n return this as Overkit<\r\n TKeys,\r\n Items & Record<THubItemName, RegistryItem<THubItemName, ItemProps>>\r\n >;\r\n }\r\n\r\n build() {\r\n return {\r\n create: this.create.bind(this),\r\n useOverkitStore: this.useOverkitStore,\r\n };\r\n }\r\n}\r\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "overkit",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "Simplified overlay management system for React",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.mjs",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.mjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"build": "tsdown",
|
|
22
|
+
"test": "vitest",
|
|
23
|
+
"test:ui": "vitest --ui",
|
|
24
|
+
"test:run": "vitest run"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"react",
|
|
28
|
+
"reactjs",
|
|
29
|
+
"overlay",
|
|
30
|
+
"modal",
|
|
31
|
+
"dialog",
|
|
32
|
+
"drawer",
|
|
33
|
+
"sheet",
|
|
34
|
+
"portal",
|
|
35
|
+
"zustand",
|
|
36
|
+
"state-management",
|
|
37
|
+
"typescript"
|
|
38
|
+
],
|
|
39
|
+
"author": {
|
|
40
|
+
"name": "Jean Caiza",
|
|
41
|
+
"email": "jeanmcm@hotmail.com",
|
|
42
|
+
"url": "https://github.com/jfortez"
|
|
43
|
+
},
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/jfortez/overkit.git",
|
|
48
|
+
"directory": "lib/overkit"
|
|
49
|
+
},
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/jfortez/overkit/issues"
|
|
52
|
+
},
|
|
53
|
+
"homepage": "https://github.com/jfortez/overkit#readme",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
56
|
+
"@testing-library/react": "^16.0.0",
|
|
57
|
+
"@types/bun": "latest",
|
|
58
|
+
"@types/react": "^19.0.0",
|
|
59
|
+
"@types/react-dom": "^19.0.0",
|
|
60
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
61
|
+
"jsdom": "^25.0.0",
|
|
62
|
+
"tsdown": "^0.20.3",
|
|
63
|
+
"typescript": "^5.0.0",
|
|
64
|
+
"vitest": "^2.1.0"
|
|
65
|
+
},
|
|
66
|
+
"peerDependencies": {
|
|
67
|
+
"react": ">=18.0.0",
|
|
68
|
+
"react-dom": ">=18.0.0"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"zustand": "^5.0.0",
|
|
72
|
+
"@radix-ui/react-slot": "^1.1.0",
|
|
73
|
+
"tunnel-rat": "^0.1.0"
|
|
74
|
+
}
|
|
75
|
+
}
|