valtio-define 0.1.0
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/LICENSE.md +21 -0
- package/README.md +253 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.mjs +183 -0
- package/package.json +70 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-PRESENT Hairyf <wwu710632@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# valtio-define
|
|
2
|
+
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
[![bundle][bundle-src]][bundle-href]
|
|
6
|
+
[![JSDocs][jsdocs-src]][jsdocs-href]
|
|
7
|
+
[![License][license-src]][license-href]
|
|
8
|
+
|
|
9
|
+
⚡ Quickly create a fully functional and robust Valtio factory
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install valtio-define
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Basic Store
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { defineStore, useStore } from 'valtio-define'
|
|
23
|
+
|
|
24
|
+
const store = defineStore({
|
|
25
|
+
state: () => ({ count: 0 }),
|
|
26
|
+
actions: {
|
|
27
|
+
increment() {
|
|
28
|
+
this.count++
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
function Counter() {
|
|
34
|
+
const state = useStore(store)
|
|
35
|
+
return (
|
|
36
|
+
<div>
|
|
37
|
+
<button onClick={store.increment}>Increment</button>
|
|
38
|
+
<div>{state.count}</div>
|
|
39
|
+
</div>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### With Getters
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
const store = defineStore({
|
|
48
|
+
state: () => ({ count: 0 }),
|
|
49
|
+
getters: {
|
|
50
|
+
doubled() {
|
|
51
|
+
return this.count * 2
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
actions: {
|
|
55
|
+
increment() {
|
|
56
|
+
this.count++
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
function Counter() {
|
|
62
|
+
const state = useStore(store)
|
|
63
|
+
return (
|
|
64
|
+
<div>
|
|
65
|
+
<div>
|
|
66
|
+
Count:
|
|
67
|
+
{state.count}
|
|
68
|
+
</div>
|
|
69
|
+
<div>
|
|
70
|
+
Doubled:
|
|
71
|
+
{state.doubled}
|
|
72
|
+
</div>
|
|
73
|
+
<button onClick={store.increment}>Increment</button>
|
|
74
|
+
</div>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Async Actions with Status
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
import { defineStore, useStatus, useStore } from 'valtio-define'
|
|
83
|
+
|
|
84
|
+
const store = defineStore({
|
|
85
|
+
state: () => ({ data: null }),
|
|
86
|
+
actions: {
|
|
87
|
+
async fetchData() {
|
|
88
|
+
const response = await fetch('/api/data')
|
|
89
|
+
this.data = await response.json()
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
function DataComponent() {
|
|
95
|
+
const state = useStore(store)
|
|
96
|
+
const status = useStatus(store)
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div>
|
|
100
|
+
{status.loading && <div> Store all actions are loading...</div>}
|
|
101
|
+
{status.finished && <div> Store all actions are finished...</div>}
|
|
102
|
+
{status.error && <div> Store all actions are error...</div>}
|
|
103
|
+
|
|
104
|
+
{status.fetchData.finished && <div> Data fetched successfully...</div>}
|
|
105
|
+
{status.fetchData.error && (
|
|
106
|
+
<div>
|
|
107
|
+
{' '}
|
|
108
|
+
Error fetching data:
|
|
109
|
+
{status.fetchData.error.message}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
{state.data && <div>{JSON.stringify(state.data)}</div>}
|
|
113
|
+
<button onClick={store.fetchData}>Fetch Data</button>
|
|
114
|
+
</div>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Persistence
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
const store = defineStore(
|
|
123
|
+
{
|
|
124
|
+
state: () => ({ count: 0 }),
|
|
125
|
+
actions: {
|
|
126
|
+
increment() {
|
|
127
|
+
this.count++
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
persist: true // or { key: 'my-store', storage: localStorage, paths: ['count'] }
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
If the persist is a boolean value, it will use `structure-id` to generate a unique key for the store.
|
|
138
|
+
|
|
139
|
+
### Subscribe to Changes
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
const store = defineStore({
|
|
143
|
+
state: () => ({ count: 0 }),
|
|
144
|
+
actions: {
|
|
145
|
+
increment() {
|
|
146
|
+
this.count++
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
// Subscribe to state changes
|
|
152
|
+
const unsubscribe = store.$subscribe((state) => {
|
|
153
|
+
console.log('State changed:', state)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Subscribe to status changes
|
|
157
|
+
const unsubscribeStatus = store.$subscribe.status((status) => {
|
|
158
|
+
console.log('Status changed:', status)
|
|
159
|
+
})
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Patch State
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// Patch with object
|
|
166
|
+
store.$patch({ count: 10 })
|
|
167
|
+
|
|
168
|
+
// Patch with function
|
|
169
|
+
store.$patch((state) => {
|
|
170
|
+
state.count += 5
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Signal (JSX Component)
|
|
175
|
+
|
|
176
|
+
```tsx
|
|
177
|
+
function App() {
|
|
178
|
+
return (
|
|
179
|
+
<div>
|
|
180
|
+
{store.$signal(state => (
|
|
181
|
+
<div>
|
|
182
|
+
Count:
|
|
183
|
+
{state.count}
|
|
184
|
+
</div>
|
|
185
|
+
))}
|
|
186
|
+
{store.$signal.status(status => (
|
|
187
|
+
status.loading && <div>Loading...</div>
|
|
188
|
+
))}
|
|
189
|
+
</div>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## API
|
|
195
|
+
|
|
196
|
+
### `defineStore(store, options?)`
|
|
197
|
+
|
|
198
|
+
Creates a store with state, actions, and getters.
|
|
199
|
+
|
|
200
|
+
**Parameters:**
|
|
201
|
+
- `store.state`: Initial state object or factory function
|
|
202
|
+
- `store.actions`: Object containing action methods
|
|
203
|
+
- `store.getters`: Object containing getter methods
|
|
204
|
+
- `options.persist`: Persistence configuration (boolean or object)
|
|
205
|
+
|
|
206
|
+
**Returns:** Store instance with reactive state and actions
|
|
207
|
+
|
|
208
|
+
### `useStore(store)`
|
|
209
|
+
|
|
210
|
+
React hook that returns a snapshot of the store state.
|
|
211
|
+
|
|
212
|
+
**Parameters:**
|
|
213
|
+
- `store`: Store instance created by `defineStore`
|
|
214
|
+
|
|
215
|
+
**Returns:** Snapshot of the store state
|
|
216
|
+
|
|
217
|
+
### `useStatus(store)`
|
|
218
|
+
|
|
219
|
+
React hook that returns the status of all actions.
|
|
220
|
+
|
|
221
|
+
**Parameters:**
|
|
222
|
+
- `store`: Store instance created by `defineStore`
|
|
223
|
+
|
|
224
|
+
**Returns:** Status object with loading, finished, and error states
|
|
225
|
+
|
|
226
|
+
### `proxyWithPersistent(initialObject, options?)`
|
|
227
|
+
|
|
228
|
+
Creates a persistent proxy state.
|
|
229
|
+
|
|
230
|
+
**Parameters:**
|
|
231
|
+
- `initialObject`: Initial state object
|
|
232
|
+
- `options.key`: Storage key (auto-generated if not provided)
|
|
233
|
+
- `options.storage`: Storage instance (defaults to localStorage)
|
|
234
|
+
- `options.paths`: Array of paths to persist (defaults to all)
|
|
235
|
+
|
|
236
|
+
**Returns:** Persistent proxy state
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
[MIT](./LICENSE) License © [Hairyf](https://github.com/hairyf)
|
|
241
|
+
|
|
242
|
+
<!-- Badges -->
|
|
243
|
+
|
|
244
|
+
[npm-version-src]: https://img.shields.io/npm/v/valtio-define?style=flat&colorA=080f12&colorB=1fa669
|
|
245
|
+
[npm-version-href]: https://npmjs.com/package/valtio-define
|
|
246
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/valtio-define?style=flat&colorA=080f12&colorB=1fa669
|
|
247
|
+
[npm-downloads-href]: https://npmjs.com/package/valtio-define
|
|
248
|
+
[bundle-src]: https://img.shields.io/bundlephobia/minzip/valtio-define?style=flat&colorA=080f12&colorB=1fa669&label=minzip
|
|
249
|
+
[bundle-href]: https://bundlephobia.com/result?p=valtio-define
|
|
250
|
+
[license-src]: https://img.shields.io/github/license/hairyf/valtio-define.svg?style=flat&colorA=080f12&colorB=1fa669
|
|
251
|
+
[license-href]: https://github.com/hairyf/valtio-define/blob/main/LICENSE
|
|
252
|
+
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
|
|
253
|
+
[jsdocs-href]: https://www.jsdocs.io/package/valtio-define
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Snapshot } from "valtio";
|
|
2
|
+
|
|
3
|
+
//#region src/types/index.d.ts
|
|
4
|
+
type Actions<S> = Record<string, (this: S, ...args: any) => any>;
|
|
5
|
+
type Getters<S> = Record<string, (this: S) => any>;
|
|
6
|
+
type ActionsOmitThisParameter<A extends Actions<any>> = { [K in keyof A]: (...args: Parameters<A[K]>) => ReturnType<A[K]> };
|
|
7
|
+
interface Status {
|
|
8
|
+
finished: boolean;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
error: Error | null;
|
|
11
|
+
}
|
|
12
|
+
type ActionsStatus<A extends Actions<any>> = Status & { [K in keyof A]: Status };
|
|
13
|
+
type GettersReturnType<G extends Getters<any>> = { [K in keyof G]: ReturnType<G[K]> };
|
|
14
|
+
interface StoreDefine<S extends object, A extends Actions<S>, G extends Getters<S>> {
|
|
15
|
+
state: (() => S) | S;
|
|
16
|
+
actions?: A;
|
|
17
|
+
getters?: G;
|
|
18
|
+
}
|
|
19
|
+
interface StoreOptions {
|
|
20
|
+
persist?: boolean | PersistentOptions;
|
|
21
|
+
}
|
|
22
|
+
interface StoreSignal<S, A extends Actions<S>, G extends Getters<S>> {
|
|
23
|
+
<T>(fn: (state: S & GettersReturnType<G>) => T): T;
|
|
24
|
+
status: <T>(fn: (status: ActionsStatus<A>) => T) => T;
|
|
25
|
+
}
|
|
26
|
+
interface StoreSubscribe<S, A extends Actions<S>, G extends Getters<S>> {
|
|
27
|
+
(listener: (state: S & GettersReturnType<G>) => void): () => void;
|
|
28
|
+
status: (listener: (status: ActionsStatus<A>) => void) => () => void;
|
|
29
|
+
}
|
|
30
|
+
interface StorePatch<S, G extends Getters<S>> {
|
|
31
|
+
(patch: Partial<S> | ((state: S & GettersReturnType<G>) => void)): void;
|
|
32
|
+
}
|
|
33
|
+
type Store<S, A extends Actions<S>, G extends Getters<S>> = {
|
|
34
|
+
$subscribe: StoreSubscribe<S, A, G>;
|
|
35
|
+
$patch: StorePatch<S, G>;
|
|
36
|
+
$state: S & GettersReturnType<G> & ActionsOmitThisParameter<A>;
|
|
37
|
+
$actions: ActionsOmitThisParameter<A>;
|
|
38
|
+
$getters: GettersReturnType<G>;
|
|
39
|
+
$status: ActionsStatus<A>;
|
|
40
|
+
$signal: StoreSignal<S, A, G>;
|
|
41
|
+
} & ActionsOmitThisParameter<A>;
|
|
42
|
+
interface PersistentOptions {
|
|
43
|
+
key?: string;
|
|
44
|
+
storage?: Storage;
|
|
45
|
+
paths?: string[];
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/define-store.d.ts
|
|
49
|
+
/**
|
|
50
|
+
* @description Define a store
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* const store = defineStore({
|
|
54
|
+
* state: () => ({ count: 0 }),
|
|
55
|
+
* actions: {
|
|
56
|
+
* increment() {
|
|
57
|
+
* this.count++
|
|
58
|
+
* },
|
|
59
|
+
* },
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* store.increment()
|
|
63
|
+
* console.log(store.$state.count) // 1
|
|
64
|
+
*
|
|
65
|
+
* function Component() {
|
|
66
|
+
* const store = useStore(store)
|
|
67
|
+
* return (
|
|
68
|
+
* <div>
|
|
69
|
+
* <button onClick={store.increment}>Increment</button>
|
|
70
|
+
* <div>{store.count}</div>
|
|
71
|
+
* </div>
|
|
72
|
+
* )
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
declare function defineStore<S extends object, A extends Actions<S>, G extends Getters<S>>(store: StoreDefine<S, A, G>, options?: StoreOptions): Store<S, A, G>;
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/persistent.d.ts
|
|
80
|
+
declare function proxyWithPersistent<T extends object>(initialObject: T, options?: PersistentOptions): T;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region src/use-status.d.ts
|
|
83
|
+
declare function useStatus<S extends object, A extends Actions<S>, G extends Getters<S>>(store: Store<S, A, G>): Snapshot<ActionsStatus<A>>;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/use-store.d.ts
|
|
86
|
+
declare function useStore<S extends object, A extends Actions<S>, G extends Getters<S>>(store: Store<S, A, G>): Snapshot<S & GettersReturnType<G>>;
|
|
87
|
+
//#endregion
|
|
88
|
+
export { defineStore, proxyWithPersistent, useStatus, useStore };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { createElement } from "react";
|
|
2
|
+
import { proxy, subscribe, useSnapshot } from "valtio";
|
|
3
|
+
import { jsonTryParse } from "@hairy/utils";
|
|
4
|
+
import { generateStructureId } from "structure-id";
|
|
5
|
+
|
|
6
|
+
//#region src/utils/index.ts
|
|
7
|
+
function track(action, status) {
|
|
8
|
+
let loadings = 0;
|
|
9
|
+
const tracking = () => {
|
|
10
|
+
loadings++ === 0 && (status.loading = true);
|
|
11
|
+
};
|
|
12
|
+
const done = () => {
|
|
13
|
+
!--loadings && (status.loading = false);
|
|
14
|
+
};
|
|
15
|
+
const fulfilled = (value) => {
|
|
16
|
+
status.finished = true;
|
|
17
|
+
done();
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
20
|
+
const rejected = (error) => {
|
|
21
|
+
status.error = error;
|
|
22
|
+
done();
|
|
23
|
+
throw error;
|
|
24
|
+
};
|
|
25
|
+
return function(...args) {
|
|
26
|
+
tracking();
|
|
27
|
+
try {
|
|
28
|
+
const value = action(...args);
|
|
29
|
+
return value instanceof Promise ? value.then(fulfilled, rejected) : fulfilled(value);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
rejected(error);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function get(obj, path) {
|
|
36
|
+
return path.split(".").reduce((result, key) => result?.[key], obj);
|
|
37
|
+
}
|
|
38
|
+
function set(obj, path, value) {
|
|
39
|
+
const keys = path.split(".");
|
|
40
|
+
const lastKey = keys.pop();
|
|
41
|
+
const target = keys.reduce((result, key) => result[key] ??= {}, obj);
|
|
42
|
+
target[lastKey] = value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/persistent.ts
|
|
47
|
+
function proxyWithPersistent(initialObject, options = {}) {
|
|
48
|
+
options.key = options.key || generateStructureId(initialObject);
|
|
49
|
+
const storage = options.storage || (typeof localStorage !== "undefined" ? localStorage : void 0);
|
|
50
|
+
const state = proxy(jsonTryParse(storage?.getItem(options.key)) || initialObject);
|
|
51
|
+
subscribe(state, () => {
|
|
52
|
+
const paths = options.paths || Object.keys(state);
|
|
53
|
+
const statePaths = {};
|
|
54
|
+
for (const key of paths) set(statePaths, key, get(state, key));
|
|
55
|
+
storage?.setItem(options.key, JSON.stringify(statePaths));
|
|
56
|
+
});
|
|
57
|
+
return state;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/define-store.ts
|
|
62
|
+
/**
|
|
63
|
+
* @description Define a store
|
|
64
|
+
* @example
|
|
65
|
+
* ```tsx
|
|
66
|
+
* const store = defineStore({
|
|
67
|
+
* state: () => ({ count: 0 }),
|
|
68
|
+
* actions: {
|
|
69
|
+
* increment() {
|
|
70
|
+
* this.count++
|
|
71
|
+
* },
|
|
72
|
+
* },
|
|
73
|
+
* })
|
|
74
|
+
*
|
|
75
|
+
* store.increment()
|
|
76
|
+
* console.log(store.$state.count) // 1
|
|
77
|
+
*
|
|
78
|
+
* function Component() {
|
|
79
|
+
* const store = useStore(store)
|
|
80
|
+
* return (
|
|
81
|
+
* <div>
|
|
82
|
+
* <button onClick={store.increment}>Increment</button>
|
|
83
|
+
* <div>{store.count}</div>
|
|
84
|
+
* </div>
|
|
85
|
+
* )
|
|
86
|
+
* }
|
|
87
|
+
*
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
function defineStore(store, options = {}) {
|
|
91
|
+
const state = typeof store.state === "function" ? store.state() : store.state;
|
|
92
|
+
const getters = store.getters || {};
|
|
93
|
+
const actions = store.actions || {};
|
|
94
|
+
const status = {};
|
|
95
|
+
status.finished = false;
|
|
96
|
+
status.loading = false;
|
|
97
|
+
status.error = null;
|
|
98
|
+
const $status = proxy(status);
|
|
99
|
+
const $state = options.persist ? proxyWithPersistent(state, options.persist === true ? {} : options.persist) : proxy(state);
|
|
100
|
+
const $actions = {};
|
|
101
|
+
const $getters = {};
|
|
102
|
+
setupActions($state, actions, $actions, $status);
|
|
103
|
+
setupGetters(state, $state, getters, $getters);
|
|
104
|
+
setupStatus($actions, $status);
|
|
105
|
+
function $subscribe(listener) {
|
|
106
|
+
return subscribe($state, () => listener($state));
|
|
107
|
+
}
|
|
108
|
+
$subscribe.status = function(listener) {
|
|
109
|
+
return subscribe($status, () => listener($status));
|
|
110
|
+
};
|
|
111
|
+
function $patch(patch) {
|
|
112
|
+
if (typeof patch === "function") patch($state);
|
|
113
|
+
else Object.assign($state, patch);
|
|
114
|
+
}
|
|
115
|
+
function $signal(fn) {
|
|
116
|
+
return createElement(() => fn(useSnapshot($state)));
|
|
117
|
+
}
|
|
118
|
+
$signal.status = function(fn) {
|
|
119
|
+
return createElement(() => fn(useSnapshot($status)));
|
|
120
|
+
};
|
|
121
|
+
return {
|
|
122
|
+
$subscribe,
|
|
123
|
+
$patch,
|
|
124
|
+
$state,
|
|
125
|
+
$status,
|
|
126
|
+
$actions,
|
|
127
|
+
$getters,
|
|
128
|
+
$signal,
|
|
129
|
+
...$actions
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function setupActions($state, actions, $actions, $status) {
|
|
133
|
+
for (const key in actions) {
|
|
134
|
+
$status[key] = {
|
|
135
|
+
finished: false,
|
|
136
|
+
loading: false,
|
|
137
|
+
error: null
|
|
138
|
+
};
|
|
139
|
+
$actions[key] = track(actions[key].bind($state), $status[key]);
|
|
140
|
+
Object.defineProperty($state, key, { get: () => $actions[key] });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function setupGetters(state, $state, getters, $getters) {
|
|
144
|
+
for (const key in getters) {
|
|
145
|
+
Object.defineProperty($getters, key, {
|
|
146
|
+
get: () => state[key],
|
|
147
|
+
enumerable: true
|
|
148
|
+
});
|
|
149
|
+
Object.defineProperty(state, key, {
|
|
150
|
+
get: () => getters[key].call($state),
|
|
151
|
+
enumerable: true
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function setupStatus($actions, $status) {
|
|
156
|
+
Object.defineProperty($status, "loading", {
|
|
157
|
+
get: () => Object.keys($actions).some((key) => $status[key].loading),
|
|
158
|
+
enumerable: true
|
|
159
|
+
});
|
|
160
|
+
Object.defineProperty($status, "finished", {
|
|
161
|
+
get: () => Object.keys($actions).every((key) => $status[key].finished),
|
|
162
|
+
enumerable: true
|
|
163
|
+
});
|
|
164
|
+
Object.defineProperty($status, "error", {
|
|
165
|
+
get: () => Object.keys($actions).find((key) => $status[key].error),
|
|
166
|
+
enumerable: true
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
//#endregion
|
|
171
|
+
//#region src/use-status.ts
|
|
172
|
+
function useStatus(store) {
|
|
173
|
+
return useSnapshot(store.$status);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/use-store.ts
|
|
178
|
+
function useStore(store) {
|
|
179
|
+
return useSnapshot(store.$state);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
//#endregion
|
|
183
|
+
export { defineStore, proxyWithPersistent, useStatus, useStore };
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "valtio-define",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "⚡quickly create a fully functional and robust Valtio factory",
|
|
6
|
+
"author": "Hairyf <wwu710632@gmail.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/hairyf/valtio-define#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/hairyf/valtio-define.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": "https://github.com/hairyf/valtio-define/issues",
|
|
14
|
+
"keywords": [],
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"exports": {
|
|
17
|
+
".": "./dist/index.mjs",
|
|
18
|
+
"./package.json": "./package.json"
|
|
19
|
+
},
|
|
20
|
+
"main": "./dist/index.mjs",
|
|
21
|
+
"module": "./dist/index.mjs",
|
|
22
|
+
"types": "./dist/index.d.mts",
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"react": "^18.2.0"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@hairy/utils": "^1.39.0",
|
|
31
|
+
"structure-id": "^1.2.9",
|
|
32
|
+
"valtio": "^2.2.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@antfu/eslint-config": "^6.2.0",
|
|
36
|
+
"@antfu/ni": "^27.0.1",
|
|
37
|
+
"@antfu/utils": "^9.3.0",
|
|
38
|
+
"@types/node": "^24.10.0",
|
|
39
|
+
"@types/react": "^19.2.6",
|
|
40
|
+
"@vitejs/plugin-react": "^5.1.1",
|
|
41
|
+
"bumpp": "^10.3.1",
|
|
42
|
+
"eslint": "^9.39.1",
|
|
43
|
+
"lint-staged": "^16.2.6",
|
|
44
|
+
"react": "^19.2.0",
|
|
45
|
+
"simple-git-hooks": "^2.13.1",
|
|
46
|
+
"tinyexec": "^1.0.2",
|
|
47
|
+
"tsdown": "^0.16.0",
|
|
48
|
+
"tsx": "^4.20.6",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"vite": "^7.2.1",
|
|
51
|
+
"vitest": "^4.0.7",
|
|
52
|
+
"vitest-package-exports": "^0.1.1",
|
|
53
|
+
"yaml": "^2.8.1"
|
|
54
|
+
},
|
|
55
|
+
"simple-git-hooks": {
|
|
56
|
+
"pre-commit": "pnpm i --frozen-lockfile --ignore-scripts --offline && npx lint-staged"
|
|
57
|
+
},
|
|
58
|
+
"lint-staged": {
|
|
59
|
+
"*": "eslint --fix"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "tsdown",
|
|
63
|
+
"dev": "tsdown --watch",
|
|
64
|
+
"lint": "eslint",
|
|
65
|
+
"release": "bumpp",
|
|
66
|
+
"start": "tsx src/index.ts",
|
|
67
|
+
"test": "vitest",
|
|
68
|
+
"typecheck": "tsc"
|
|
69
|
+
}
|
|
70
|
+
}
|