@uistate/core 5.0.2 → 5.0.3
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/examples/002-counter-improved/index.html +4 -1
- package/examples/003-input-reactive/index.html +4 -1
- package/examples/004-computed-state/index.html +9 -6
- package/examples/005-conditional-rendering/index.html +7 -4
- package/examples/006-list-rendering/index.html +8 -5
- package/examples/007-form-validation/index.html +11 -8
- package/examples/008-undo-redo/index.html +15 -12
- package/examples/009-localStorage-side-effects/index.html +7 -4
- package/examples/010-decoupled-components/index.html +6 -3
- package/examples/011-async-patterns/index.html +19 -16
- package/package.json +1 -1
- package/examples/002-counter-improved/eventState.js +0 -86
- package/examples/003-input-reactive/eventState.js +0 -86
- package/examples/004-computed-state/eventState.js +0 -86
- package/examples/005-conditional-rendering/eventState.js +0 -86
- package/examples/006-list-rendering/eventState.js +0 -86
- package/examples/007-form-validation/eventState.js +0 -86
- package/examples/008-undo-redo/eventState.js +0 -86
- package/examples/009-localStorage-side-effects/eventState.js +0 -86
- package/examples/010-decoupled-components/eventState.js +0 -86
- package/examples/011-async-patterns/eventState.js +0 -86
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>002 Counter Improved - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Counter: <span id="count">0</span></h1>
|
|
@@ -12,7 +15,7 @@
|
|
|
12
15
|
<button id="double">x2</button>
|
|
13
16
|
|
|
14
17
|
<script type="module">
|
|
15
|
-
import { createEventState } from '
|
|
18
|
+
import { createEventState } from '@uistate/core';
|
|
16
19
|
|
|
17
20
|
// Create store
|
|
18
21
|
const store = createEventState({ count: 0 });
|
|
@@ -4,6 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>003 Input Reactive - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Type something:</h1>
|
|
@@ -11,7 +14,7 @@
|
|
|
11
14
|
<p id="output"></p>
|
|
12
15
|
|
|
13
16
|
<script type="module">
|
|
14
|
-
import { createEventState } from '
|
|
17
|
+
import { createEventState } from '@uistate/core';
|
|
15
18
|
|
|
16
19
|
// Create store
|
|
17
20
|
const store = createEventState({ text: '' });
|
|
@@ -4,23 +4,26 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>004 Computed State - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Computed State</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<label>First Name: <input type="text" id="firstName" placeholder="Abraham"></label><br>
|
|
12
15
|
<label>Last Name: <input type="text" id="lastName" placeholder="Lincoln"></label><br>
|
|
13
|
-
|
|
16
|
+
|
|
14
17
|
<p><strong>Full Name:</strong> <span id="fullName"></span></p>
|
|
15
18
|
<p><strong>Character Count:</strong> <span id="charCount">0</span></p>
|
|
16
19
|
|
|
17
20
|
<script type="module">
|
|
18
|
-
import { createEventState } from '
|
|
21
|
+
import { createEventState } from '@uistate/core';
|
|
19
22
|
|
|
20
23
|
// Create store
|
|
21
|
-
const store = createEventState({
|
|
22
|
-
firstName: '',
|
|
23
|
-
lastName: ''
|
|
24
|
+
const store = createEventState({
|
|
25
|
+
firstName: '',
|
|
26
|
+
lastName: ''
|
|
24
27
|
});
|
|
25
28
|
|
|
26
29
|
// Computed: full name (derived from firstName + lastName)
|
|
@@ -4,19 +4,22 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>005 Conditional Rendering - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Conditional Rendering</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<label>
|
|
12
15
|
<input type="checkbox" id="toggle">
|
|
13
16
|
Show secret message
|
|
14
17
|
</label>
|
|
15
|
-
|
|
16
|
-
<p id="message" style="display: none;"
|
|
18
|
+
|
|
19
|
+
<p id="message" style="display: none;">You found the secret message!</p>
|
|
17
20
|
|
|
18
21
|
<script type="module">
|
|
19
|
-
import { createEventState } from '
|
|
22
|
+
import { createEventState } from '@uistate/core';
|
|
20
23
|
|
|
21
24
|
// Create store
|
|
22
25
|
const store = createEventState({ isVisible: false });
|
|
@@ -4,17 +4,20 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>006 List Rendering - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Shopping List</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<input type="text" id="itemInput" placeholder="Add item...">
|
|
12
15
|
<button id="addBtn">Add</button>
|
|
13
|
-
|
|
16
|
+
|
|
14
17
|
<ul id="itemList"></ul>
|
|
15
18
|
|
|
16
19
|
<script type="module">
|
|
17
|
-
import { createEventState } from '
|
|
20
|
+
import { createEventState } from '@uistate/core';
|
|
18
21
|
|
|
19
22
|
// Create store
|
|
20
23
|
const store = createEventState({ items: [] });
|
|
@@ -23,7 +26,7 @@
|
|
|
23
26
|
const renderList = () => {
|
|
24
27
|
const items = store.get('items');
|
|
25
28
|
const list = document.getElementById('itemList');
|
|
26
|
-
|
|
29
|
+
|
|
27
30
|
if (items.length === 0) {
|
|
28
31
|
list.innerHTML = '<li><em>No items yet</em></li>';
|
|
29
32
|
} else {
|
|
@@ -38,7 +41,7 @@
|
|
|
38
41
|
document.getElementById('addBtn').onclick = () => {
|
|
39
42
|
const input = document.getElementById('itemInput');
|
|
40
43
|
const value = input.value.trim();
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
if (value) {
|
|
43
46
|
const items = store.get('items');
|
|
44
47
|
store.set('items', [...items, value]);
|
|
@@ -4,30 +4,33 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>007 Form Validation - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Sign Up Form</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<label>
|
|
12
15
|
Email:
|
|
13
16
|
<input type="email" id="email" placeholder="you@example.com">
|
|
14
17
|
</label>
|
|
15
18
|
<p id="emailError" style="color: red;"></p>
|
|
16
|
-
|
|
19
|
+
|
|
17
20
|
<label>
|
|
18
21
|
Password:
|
|
19
22
|
<input type="password" id="password" placeholder="Min 8 characters">
|
|
20
23
|
</label>
|
|
21
24
|
<p id="passwordError" style="color: red;"></p>
|
|
22
|
-
|
|
25
|
+
|
|
23
26
|
<button id="submitBtn" disabled>Submit</button>
|
|
24
27
|
<p id="submitMessage" style="color: green;"></p>
|
|
25
28
|
|
|
26
29
|
<script type="module">
|
|
27
|
-
import { createEventState } from '
|
|
30
|
+
import { createEventState } from '@uistate/core';
|
|
28
31
|
|
|
29
32
|
// Create store
|
|
30
|
-
const store = createEventState({
|
|
33
|
+
const store = createEventState({
|
|
31
34
|
email: '',
|
|
32
35
|
password: '',
|
|
33
36
|
emailValid: false,
|
|
@@ -55,7 +58,7 @@
|
|
|
55
58
|
store.subscribe('email', (value) => {
|
|
56
59
|
const isValid = validateEmail(value);
|
|
57
60
|
store.set('emailValid', isValid);
|
|
58
|
-
|
|
61
|
+
|
|
59
62
|
const error = document.getElementById('emailError');
|
|
60
63
|
error.textContent = value && !isValid ? 'Invalid email address' : '';
|
|
61
64
|
});
|
|
@@ -64,7 +67,7 @@
|
|
|
64
67
|
store.subscribe('password', (value) => {
|
|
65
68
|
const isValid = validatePassword(value);
|
|
66
69
|
store.set('passwordValid', isValid);
|
|
67
|
-
|
|
70
|
+
|
|
68
71
|
const error = document.getElementById('passwordError');
|
|
69
72
|
error.textContent = value && !isValid ? 'Password must be at least 8 characters' : '';
|
|
70
73
|
});
|
|
@@ -87,7 +90,7 @@
|
|
|
87
90
|
const email = store.get('email');
|
|
88
91
|
const message = document.getElementById('submitMessage');
|
|
89
92
|
message.textContent = `✓ Form submitted for ${email}`;
|
|
90
|
-
|
|
93
|
+
|
|
91
94
|
// Clear form
|
|
92
95
|
document.getElementById('email').value = '';
|
|
93
96
|
document.getElementById('password').value = '';
|
|
@@ -4,25 +4,28 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>008 Undo/Redo - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Counter with Time Travel</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<h2>Count: <span id="count">0</span></h2>
|
|
12
|
-
|
|
15
|
+
|
|
13
16
|
<button id="increment">+1</button>
|
|
14
17
|
<button id="decrement">-1</button>
|
|
15
18
|
<button id="double">×2</button>
|
|
16
|
-
|
|
19
|
+
|
|
17
20
|
<hr>
|
|
18
|
-
|
|
21
|
+
|
|
19
22
|
<button id="undo" disabled>⟲ Undo</button>
|
|
20
23
|
<button id="redo" disabled>⟳ Redo</button>
|
|
21
|
-
|
|
24
|
+
|
|
22
25
|
<p><small>History: <span id="historyInfo">0 states</span></small></p>
|
|
23
26
|
|
|
24
27
|
<script type="module">
|
|
25
|
-
import { createEventState } from '
|
|
28
|
+
import { createEventState } from '@uistate/core';
|
|
26
29
|
|
|
27
30
|
// Create store
|
|
28
31
|
const store = createEventState({ count: 0 });
|
|
@@ -36,29 +39,29 @@
|
|
|
36
39
|
const updateUI = () => {
|
|
37
40
|
const count = store.get('count');
|
|
38
41
|
document.getElementById('count').textContent = count;
|
|
39
|
-
|
|
42
|
+
|
|
40
43
|
// Update button states
|
|
41
44
|
document.getElementById('undo').disabled = historyIndex === 0;
|
|
42
45
|
document.getElementById('redo').disabled = historyIndex === history.length - 1;
|
|
43
|
-
|
|
46
|
+
|
|
44
47
|
// Update history info
|
|
45
|
-
document.getElementById('historyInfo').textContent =
|
|
48
|
+
document.getElementById('historyInfo').textContent =
|
|
46
49
|
`${historyIndex + 1} of ${history.length} states`;
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
// Subscribe to count changes and record history
|
|
50
53
|
store.subscribe('count', (value) => {
|
|
51
54
|
updateUI();
|
|
52
|
-
|
|
55
|
+
|
|
53
56
|
// Only record if not time traveling
|
|
54
57
|
if (!isTimeTravel) {
|
|
55
58
|
// Remove any "future" states if we're in the middle of history
|
|
56
59
|
history = history.slice(0, historyIndex + 1);
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
// Add new state
|
|
59
62
|
history.push(value);
|
|
60
63
|
historyIndex = history.length - 1;
|
|
61
|
-
|
|
64
|
+
|
|
62
65
|
updateUI();
|
|
63
66
|
}
|
|
64
67
|
});
|
|
@@ -4,20 +4,23 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>009 localStorage Side Effects - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Persistent Counter</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<h2>Count: <span id="count">0</span></h2>
|
|
12
|
-
|
|
15
|
+
|
|
13
16
|
<button id="increment">+1</button>
|
|
14
17
|
<button id="decrement">-1</button>
|
|
15
18
|
<button id="reset">Reset</button>
|
|
16
|
-
|
|
19
|
+
|
|
17
20
|
<p><small>Reload the page - your count persists!</small></p>
|
|
18
21
|
|
|
19
22
|
<script type="module">
|
|
20
|
-
import { createEventState } from '
|
|
23
|
+
import { createEventState } from '@uistate/core';
|
|
21
24
|
|
|
22
25
|
const STORAGE_KEY = 'eventstate-counter';
|
|
23
26
|
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
margin: 10px 0;
|
|
12
12
|
}
|
|
13
13
|
</style>
|
|
14
|
+
<script type="importmap">
|
|
15
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
16
|
+
</script>
|
|
14
17
|
</head>
|
|
15
18
|
<body>
|
|
16
19
|
<h1>Decoupled Components</h1>
|
|
@@ -37,10 +40,10 @@
|
|
|
37
40
|
</div>
|
|
38
41
|
|
|
39
42
|
<script type="module">
|
|
40
|
-
import { createEventState } from '
|
|
43
|
+
import { createEventState } from '@uistate/core';
|
|
41
44
|
|
|
42
45
|
// Shared store - the ONLY connection between components
|
|
43
|
-
const store = createEventState({
|
|
46
|
+
const store = createEventState({
|
|
44
47
|
message: '',
|
|
45
48
|
messageCount: 0
|
|
46
49
|
});
|
|
@@ -50,7 +53,7 @@
|
|
|
50
53
|
document.getElementById('sendBtn').onclick = () => {
|
|
51
54
|
const input = document.getElementById('messageInput');
|
|
52
55
|
const value = input.value.trim();
|
|
53
|
-
|
|
56
|
+
|
|
54
57
|
if (value) {
|
|
55
58
|
store.set('message', value);
|
|
56
59
|
store.set('messageCount', store.get('messageCount') + 1);
|
|
@@ -4,27 +4,30 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>011 Async Patterns - EventState</title>
|
|
7
|
+
<script type="importmap">
|
|
8
|
+
{ "imports": { "@uistate/core": "../../index.js" } }
|
|
9
|
+
</script>
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<h1>Async Patterns</h1>
|
|
10
|
-
|
|
13
|
+
|
|
11
14
|
<h2>Debounced Search</h2>
|
|
12
15
|
<input type="text" id="searchInput" placeholder="Type to search...">
|
|
13
16
|
<p id="searchStatus"></p>
|
|
14
17
|
<p><strong>Search result:</strong> <span id="searchResult">(type something)</span></p>
|
|
15
|
-
|
|
18
|
+
|
|
16
19
|
<hr>
|
|
17
|
-
|
|
20
|
+
|
|
18
21
|
<h2>Simulated API Call</h2>
|
|
19
22
|
<button id="fetchBtn">Fetch Data</button>
|
|
20
23
|
<p id="loadingStatus"></p>
|
|
21
24
|
<p><strong>Data:</strong> <span id="apiData">(not loaded)</span></p>
|
|
22
25
|
|
|
23
26
|
<script type="module">
|
|
24
|
-
import { createEventState } from '
|
|
27
|
+
import { createEventState } from '@uistate/core';
|
|
25
28
|
|
|
26
29
|
// Create store
|
|
27
|
-
const store = createEventState({
|
|
30
|
+
const store = createEventState({
|
|
28
31
|
searchQuery: '',
|
|
29
32
|
searchResult: '',
|
|
30
33
|
isSearching: false,
|
|
@@ -48,16 +51,16 @@
|
|
|
48
51
|
store.subscribe('searchQuery', async (value) => {
|
|
49
52
|
// Clear previous timeout
|
|
50
53
|
clearTimeout(searchTimeout);
|
|
51
|
-
|
|
54
|
+
|
|
52
55
|
if (!value) {
|
|
53
56
|
store.set('searchResult', '');
|
|
54
57
|
store.set('isSearching', false);
|
|
55
58
|
return;
|
|
56
59
|
}
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
// Show searching indicator
|
|
59
62
|
store.set('isSearching', true);
|
|
60
|
-
|
|
63
|
+
|
|
61
64
|
// Debounce: wait 300ms before searching
|
|
62
65
|
searchTimeout = setTimeout(async () => {
|
|
63
66
|
const result = await performSearch(value);
|
|
@@ -68,12 +71,12 @@
|
|
|
68
71
|
|
|
69
72
|
// Update UI for search status
|
|
70
73
|
store.subscribe('isSearching', (value) => {
|
|
71
|
-
document.getElementById('searchStatus').textContent =
|
|
72
|
-
value ? '
|
|
74
|
+
document.getElementById('searchStatus').textContent =
|
|
75
|
+
value ? 'Searching...' : '';
|
|
73
76
|
});
|
|
74
77
|
|
|
75
78
|
store.subscribe('searchResult', (value) => {
|
|
76
|
-
document.getElementById('searchResult').textContent =
|
|
79
|
+
document.getElementById('searchResult').textContent =
|
|
77
80
|
value || '(type something)';
|
|
78
81
|
});
|
|
79
82
|
|
|
@@ -83,12 +86,12 @@
|
|
|
83
86
|
};
|
|
84
87
|
|
|
85
88
|
// ===== SIMULATED API CALL =====
|
|
86
|
-
|
|
89
|
+
|
|
87
90
|
// Simulate API fetch
|
|
88
91
|
const fetchData = () => {
|
|
89
92
|
return new Promise((resolve) => {
|
|
90
93
|
setTimeout(() => {
|
|
91
|
-
resolve({
|
|
94
|
+
resolve({
|
|
92
95
|
id: Math.floor(Math.random() * 1000),
|
|
93
96
|
timestamp: new Date().toLocaleTimeString()
|
|
94
97
|
});
|
|
@@ -98,7 +101,7 @@
|
|
|
98
101
|
|
|
99
102
|
// Subscribe to loading state
|
|
100
103
|
store.subscribe('isLoading', (value) => {
|
|
101
|
-
document.getElementById('loadingStatus').textContent =
|
|
104
|
+
document.getElementById('loadingStatus').textContent =
|
|
102
105
|
value ? '⏳ Loading...' : '';
|
|
103
106
|
document.getElementById('fetchBtn').disabled = value;
|
|
104
107
|
});
|
|
@@ -106,7 +109,7 @@
|
|
|
106
109
|
// Subscribe to API data
|
|
107
110
|
store.subscribe('apiData', (value) => {
|
|
108
111
|
if (value) {
|
|
109
|
-
document.getElementById('apiData').textContent =
|
|
112
|
+
document.getElementById('apiData').textContent =
|
|
110
113
|
`ID: ${value.id}, Time: ${value.timestamp}`;
|
|
111
114
|
}
|
|
112
115
|
});
|
|
@@ -114,7 +117,7 @@
|
|
|
114
117
|
// Fetch button
|
|
115
118
|
document.getElementById('fetchBtn').onclick = async () => {
|
|
116
119
|
store.set('isLoading', true);
|
|
117
|
-
|
|
120
|
+
|
|
118
121
|
try {
|
|
119
122
|
const data = await fetchData();
|
|
120
123
|
store.set('apiData', data);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uistate/core",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.3",
|
|
4
4
|
"description": "Lightweight event-driven state management with slot orchestration and experimental event-sequence testing (eventTest.js available under dual license)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
const createEventState = (initial = {}) => {
|
|
2
|
-
const store = JSON.parse(JSON.stringify(initial));
|
|
3
|
-
|
|
4
|
-
const bus = new EventTarget();
|
|
5
|
-
let destroyed = false;
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
get: (path) => {
|
|
9
|
-
if (destroyed) throw new Error('Cannot get from destroyed store');
|
|
10
|
-
if (!path) return store;
|
|
11
|
-
|
|
12
|
-
// Parse dot-paths: 'items.0' → ['items', '0']
|
|
13
|
-
const parts = path.split('.').flatMap(part => {
|
|
14
|
-
const match = part.match(/([^\[]+)\[(\d+)\]/);
|
|
15
|
-
return match ? [match[1], match[2]] : part;
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
return parts.reduce(
|
|
19
|
-
(obj, prop) => (obj && obj[prop] !== undefined ? obj[prop] : undefined),
|
|
20
|
-
store
|
|
21
|
-
);
|
|
22
|
-
},
|
|
23
|
-
set: (path, value) => {
|
|
24
|
-
if (destroyed) throw new Error('Cannot set on destroyed store');
|
|
25
|
-
if (!path) return;
|
|
26
|
-
const parts = path.split('.');
|
|
27
|
-
const last = parts.pop();
|
|
28
|
-
let target = store;
|
|
29
|
-
|
|
30
|
-
for (const key of parts) {
|
|
31
|
-
// create intermediate objects as needed
|
|
32
|
-
if (typeof target[key] !== 'object' || target[key] === null) {
|
|
33
|
-
target[key] = {};
|
|
34
|
-
}
|
|
35
|
-
target = target[key];
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
target[last] = value;
|
|
39
|
-
|
|
40
|
-
if (!destroyed) {
|
|
41
|
-
// exact path
|
|
42
|
-
bus.dispatchEvent(new CustomEvent(path, { detail: value }));
|
|
43
|
-
|
|
44
|
-
// parent wildcards: a, a.b -> 'a.*', 'a.b.*'
|
|
45
|
-
for (let i = 1; i <= parts.length; i++) {
|
|
46
|
-
const parent = parts.slice(0, i).join('.');
|
|
47
|
-
bus.dispatchEvent(new CustomEvent(`${parent}.*`, { detail: { path, value } }));
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// root wildcard
|
|
51
|
-
bus.dispatchEvent(new CustomEvent('*', { detail: { path, value } }));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return value;
|
|
55
|
-
},
|
|
56
|
-
subscribe(path, handler) {
|
|
57
|
-
if (destroyed) throw new Error('store destroyed');
|
|
58
|
-
if (!path || typeof handler !== 'function') {
|
|
59
|
-
throw new TypeError('subscribe(path, handler) requires a string path and function handler');
|
|
60
|
-
}
|
|
61
|
-
const onEvent = (evt) => handler(evt.detail, evt);
|
|
62
|
-
bus.addEventListener(path, onEvent);
|
|
63
|
-
|
|
64
|
-
return function unsubscribe() {
|
|
65
|
-
bus.removeEventListener(path, onEvent);
|
|
66
|
-
};
|
|
67
|
-
},
|
|
68
|
-
off(unsubscribe) {
|
|
69
|
-
if (typeof unsubscribe !== 'function') {
|
|
70
|
-
throw new TypeError('off(unsubscribe) requires a function returned by subscribe');
|
|
71
|
-
}
|
|
72
|
-
return unsubscribe();
|
|
73
|
-
},
|
|
74
|
-
|
|
75
|
-
destroy() {
|
|
76
|
-
if (!destroyed) {
|
|
77
|
-
destroyed = true;
|
|
78
|
-
// EventTarget has no parentNode - just mark as destroyed
|
|
79
|
-
// Future sets/subscribes will be blocked by destroyed flag
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export default createEventState;
|
|
86
|
-
export { createEventState };
|