@mhmo91/schmancy 0.2.193 → 0.2.194
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/ai/area.md +125 -0
- package/ai/autocomplete.md +135 -0
- package/ai/avatar.md +178 -0
- package/ai/badge.md +100 -0
- package/ai/busy.md +195 -0
- package/ai/button.md +112 -0
- package/ai/card.md +155 -0
- package/ai/checkbox.md +108 -0
- package/ai/chips.md +180 -0
- package/ai/component-relationships.md +93 -0
- package/ai/dialog.md +109 -0
- package/ai/dropdown.md +238 -0
- package/ai/form.md +148 -0
- package/ai/icons.md +147 -0
- package/ai/index.md +71 -0
- package/ai/input.md +167 -0
- package/ai/layout.md +166 -0
- package/ai/list.md +145 -0
- package/ai/menu.md +217 -0
- package/ai/notification.md +93 -0
- package/ai/radio-group.md +176 -0
- package/ai/select.md +174 -0
- package/ai/sheet.md +122 -0
- package/ai/store.md +235 -0
- package/ai/surface.md +221 -0
- package/ai/table.md +236 -0
- package/ai/tabs.md +71 -0
- package/ai/template.md +144 -0
- package/ai/textarea.md +147 -0
- package/ai/tooltip.md +149 -0
- package/ai/tree.md +279 -0
- package/ai/typography.md +170 -0
- package/dist/ai/area.md +125 -0
- package/dist/ai/autocomplete.md +135 -0
- package/dist/ai/avatar.md +178 -0
- package/dist/ai/badge.md +100 -0
- package/dist/ai/busy.md +195 -0
- package/dist/ai/button.md +112 -0
- package/dist/ai/card.md +155 -0
- package/dist/ai/checkbox.md +108 -0
- package/dist/ai/chips.md +180 -0
- package/dist/ai/component-relationships.md +93 -0
- package/dist/ai/dialog.md +109 -0
- package/dist/ai/dropdown.md +238 -0
- package/dist/ai/form.md +148 -0
- package/dist/ai/icons.md +147 -0
- package/dist/ai/index.md +71 -0
- package/dist/ai/input.md +167 -0
- package/dist/ai/layout.md +166 -0
- package/dist/ai/list.md +145 -0
- package/dist/ai/menu.md +217 -0
- package/dist/ai/notification.md +93 -0
- package/dist/ai/radio-group.md +176 -0
- package/dist/ai/select.md +174 -0
- package/dist/ai/sheet.md +122 -0
- package/dist/ai/store.md +235 -0
- package/dist/ai/surface.md +221 -0
- package/dist/ai/table.md +236 -0
- package/dist/ai/tabs.md +71 -0
- package/dist/ai/template.md +144 -0
- package/dist/ai/textarea.md +147 -0
- package/dist/ai/tooltip.md +149 -0
- package/dist/ai/tree.md +279 -0
- package/dist/ai/typography.md +170 -0
- package/dist/area.cjs +1 -1
- package/dist/area.component-CzFJM7Y4.js +143 -0
- package/dist/area.component-CzFJM7Y4.js.map +1 -0
- package/dist/area.component-WxccFh1z.cjs +8 -0
- package/dist/area.component-WxccFh1z.cjs.map +1 -0
- package/dist/area.js +1 -1
- package/dist/{avatar-s-2jSsaH.cjs → avatar-Bmg5TXj9.cjs} +2 -2
- package/dist/{avatar-s-2jSsaH.cjs.map → avatar-Bmg5TXj9.cjs.map} +1 -1
- package/dist/{avatar-WAIlaNJg.js → avatar-CgP1tBZq.js} +2 -2
- package/dist/{avatar-WAIlaNJg.js.map → avatar-CgP1tBZq.js.map} +1 -1
- package/dist/badge.cjs +1 -1
- package/dist/badge.js +1 -1
- package/dist/card.cjs +1 -1
- package/dist/card.js +1 -1
- package/dist/content-drawer.cjs +1 -1
- package/dist/content-drawer.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +2 -2
- package/dist/nav-drawer.cjs +1 -1
- package/dist/nav-drawer.js +1 -1
- package/dist/teleport.cjs +1 -1
- package/dist/teleport.js +1 -1
- package/package.json +2 -1
- package/types/src/area/area.service.d.ts +53 -4
- package/types/src/area/router.types.d.ts +41 -2
- package/dist/area.component-CZELEuMj.js +0 -107
- package/dist/area.component-CZELEuMj.js.map +0 -1
- package/dist/area.component-grRkXEse.cjs +0 -8
- package/dist/area.component-grRkXEse.cjs.map +0 -1
package/ai/select.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Schmancy Select - AI Reference
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
// Basic Select
|
|
5
|
+
<schmancy-select
|
|
6
|
+
name="select-name"
|
|
7
|
+
label="Select Label"
|
|
8
|
+
value="selected-value"
|
|
9
|
+
placeholder="Choose an option"
|
|
10
|
+
required?
|
|
11
|
+
disabled?
|
|
12
|
+
error="Error message"
|
|
13
|
+
@change=${handleChange}>
|
|
14
|
+
|
|
15
|
+
<schmancy-option value="option1">Option 1</schmancy-option>
|
|
16
|
+
<schmancy-option value="option2">Option 2</schmancy-option>
|
|
17
|
+
<schmancy-option value="option3" disabled>Option 3</schmancy-option>
|
|
18
|
+
</schmancy-select>
|
|
19
|
+
|
|
20
|
+
// Select with helper text
|
|
21
|
+
<schmancy-select
|
|
22
|
+
label="Country"
|
|
23
|
+
helper-text="Select your country of residence"
|
|
24
|
+
required>
|
|
25
|
+
<!-- Options -->
|
|
26
|
+
</schmancy-select>
|
|
27
|
+
|
|
28
|
+
// Multiple select
|
|
29
|
+
<schmancy-select
|
|
30
|
+
multiple
|
|
31
|
+
label="Select Skills"
|
|
32
|
+
.value=${['html', 'css']}
|
|
33
|
+
@change=${(e) => console.log('Selected:', e.detail.value)}>
|
|
34
|
+
|
|
35
|
+
<schmancy-option value="html">HTML</schmancy-option>
|
|
36
|
+
<schmancy-option value="css">CSS</schmancy-option>
|
|
37
|
+
<schmancy-option value="js">JavaScript</schmancy-option>
|
|
38
|
+
<schmancy-option value="ts">TypeScript</schmancy-option>
|
|
39
|
+
</schmancy-select>
|
|
40
|
+
|
|
41
|
+
// Select with option groups
|
|
42
|
+
<schmancy-select label="Programming Language">
|
|
43
|
+
<optgroup label="Frontend">
|
|
44
|
+
<schmancy-option value="html">HTML</schmancy-option>
|
|
45
|
+
<schmancy-option value="css">CSS</schmancy-option>
|
|
46
|
+
<schmancy-option value="js">JavaScript</schmancy-option>
|
|
47
|
+
</optgroup>
|
|
48
|
+
|
|
49
|
+
<optgroup label="Backend">
|
|
50
|
+
<schmancy-option value="node">Node.js</schmancy-option>
|
|
51
|
+
<schmancy-option value="python">Python</schmancy-option>
|
|
52
|
+
<schmancy-option value="java">Java</schmancy-option>
|
|
53
|
+
</optgroup>
|
|
54
|
+
</schmancy-select>
|
|
55
|
+
|
|
56
|
+
// Select with search
|
|
57
|
+
<schmancy-select
|
|
58
|
+
searchable
|
|
59
|
+
label="Search Countries"
|
|
60
|
+
placeholder="Type to search...">
|
|
61
|
+
<!-- Many options -->
|
|
62
|
+
</schmancy-select>
|
|
63
|
+
|
|
64
|
+
// Select Methods
|
|
65
|
+
select.focus() -> void // Focus the select
|
|
66
|
+
select.blur() -> void // Remove focus
|
|
67
|
+
select.open() -> void // Open the dropdown
|
|
68
|
+
select.close() -> void // Close the dropdown
|
|
69
|
+
select.validate() -> boolean // Validate and show error if invalid
|
|
70
|
+
select.reset() -> void // Reset to initial value
|
|
71
|
+
|
|
72
|
+
// Select Properties
|
|
73
|
+
value: string | string[] // Currently selected value(s)
|
|
74
|
+
name: string // The name attribute
|
|
75
|
+
label: string // Label text
|
|
76
|
+
placeholder: string // Placeholder text
|
|
77
|
+
multiple: boolean // Whether multiple selection is allowed
|
|
78
|
+
disabled: boolean // Whether the select is disabled
|
|
79
|
+
required: boolean // Whether a selection is required
|
|
80
|
+
error: string // Error message to display
|
|
81
|
+
searchable: boolean // Whether to allow searching within options
|
|
82
|
+
open: boolean // Whether the dropdown is open
|
|
83
|
+
|
|
84
|
+
// Option Properties
|
|
85
|
+
value: string // Value of this option
|
|
86
|
+
disabled: boolean // Whether this option is disabled
|
|
87
|
+
selected: boolean // Whether this option is selected
|
|
88
|
+
|
|
89
|
+
// Select Events
|
|
90
|
+
@change // Fires when selection changes, with { detail: { value } }
|
|
91
|
+
@input // Fires during interaction
|
|
92
|
+
@focus // Fires when select gains focus
|
|
93
|
+
@blur // Fires when select loses focus
|
|
94
|
+
@open // Fires when dropdown opens
|
|
95
|
+
@close // Fires when dropdown closes
|
|
96
|
+
@search // Fires during search with { detail: { query } }
|
|
97
|
+
|
|
98
|
+
// Examples
|
|
99
|
+
// Basic usage
|
|
100
|
+
<schmancy-select
|
|
101
|
+
name="category"
|
|
102
|
+
label="Category"
|
|
103
|
+
value="electronics"
|
|
104
|
+
@change=${(e) => console.log('Selected:', e.detail.value)}>
|
|
105
|
+
|
|
106
|
+
<schmancy-option value="electronics">Electronics</schmancy-option>
|
|
107
|
+
<schmancy-option value="clothing">Clothing</schmancy-option>
|
|
108
|
+
<schmancy-option value="books">Books</schmancy-option>
|
|
109
|
+
<schmancy-option value="food">Food & Beverages</schmancy-option>
|
|
110
|
+
</schmancy-select>
|
|
111
|
+
|
|
112
|
+
// Multiple select with initial values
|
|
113
|
+
<schmancy-select
|
|
114
|
+
name="interests"
|
|
115
|
+
label="Interests"
|
|
116
|
+
multiple
|
|
117
|
+
.value=${userInterests} // Array of selected values
|
|
118
|
+
@change=${(e) => updateInterests(e.detail.value)}>
|
|
119
|
+
|
|
120
|
+
<schmancy-option value="tech">Technology</schmancy-option>
|
|
121
|
+
<schmancy-option value="sports">Sports</schmancy-option>
|
|
122
|
+
<schmancy-option value="music">Music</schmancy-option>
|
|
123
|
+
<schmancy-option value="movies">Movies</schmancy-option>
|
|
124
|
+
<schmancy-option value="travel">Travel</schmancy-option>
|
|
125
|
+
<schmancy-option value="food">Food</schmancy-option>
|
|
126
|
+
</schmancy-select>
|
|
127
|
+
|
|
128
|
+
// Searchable select with many options
|
|
129
|
+
<schmancy-select
|
|
130
|
+
name="country"
|
|
131
|
+
label="Country"
|
|
132
|
+
searchable
|
|
133
|
+
placeholder="Search for a country">
|
|
134
|
+
|
|
135
|
+
${countries.map(country => html`
|
|
136
|
+
<schmancy-option value=${country.code}>${country.name}</schmancy-option>
|
|
137
|
+
`)}
|
|
138
|
+
</schmancy-select>
|
|
139
|
+
|
|
140
|
+
// Usage in a form
|
|
141
|
+
<schmancy-form @submit=${handleSubmit}>
|
|
142
|
+
<schmancy-input name="name" label="Name" required></schmancy-input>
|
|
143
|
+
|
|
144
|
+
<schmancy-select
|
|
145
|
+
name="department"
|
|
146
|
+
label="Department"
|
|
147
|
+
required>
|
|
148
|
+
|
|
149
|
+
<schmancy-option value="hr">Human Resources</schmancy-option>
|
|
150
|
+
<schmancy-option value="engineering">Engineering</schmancy-option>
|
|
151
|
+
<schmancy-option value="marketing">Marketing</schmancy-option>
|
|
152
|
+
<schmancy-option value="finance">Finance</schmancy-option>
|
|
153
|
+
</schmancy-select>
|
|
154
|
+
|
|
155
|
+
<schmancy-button type="submit">Submit</schmancy-button>
|
|
156
|
+
</schmancy-form>
|
|
157
|
+
|
|
158
|
+
// Select with custom rendering
|
|
159
|
+
<schmancy-select
|
|
160
|
+
name="user"
|
|
161
|
+
label="Assign to User"
|
|
162
|
+
@change=${assignToUser}>
|
|
163
|
+
|
|
164
|
+
${users.map(user => html`
|
|
165
|
+
<schmancy-option value=${user.id}>
|
|
166
|
+
<div style="display: flex; align-items: center;">
|
|
167
|
+
<schmancy-avatar src=${user.avatar} size="small"></schmancy-avatar>
|
|
168
|
+
<span style="margin-left: 8px;">${user.name}</span>
|
|
169
|
+
<span style="margin-left: auto; color: gray; font-size: 0.8em;">${user.role}</span>
|
|
170
|
+
</div>
|
|
171
|
+
</schmancy-option>
|
|
172
|
+
`)}
|
|
173
|
+
</schmancy-select>
|
|
174
|
+
```
|
package/ai/sheet.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Schmancy Sheet - AI Reference
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
// Sheet Service API
|
|
5
|
+
import { $sheet } from 'schmancy';
|
|
6
|
+
|
|
7
|
+
// Open a sheet
|
|
8
|
+
$sheet.open({
|
|
9
|
+
title?: string,
|
|
10
|
+
content: TemplateResult|HTMLElement|Function,
|
|
11
|
+
width?: string|number, // default "600px"
|
|
12
|
+
height?: string|number, // default "auto"
|
|
13
|
+
position?: 'right'|'left'|'bottom'|'top', // default "right"
|
|
14
|
+
closable?: boolean, // default true
|
|
15
|
+
backdrop?: boolean, // default false
|
|
16
|
+
onClose?: Function,
|
|
17
|
+
hooks?: {
|
|
18
|
+
beforeOpen?: Function,
|
|
19
|
+
afterOpen?: Function,
|
|
20
|
+
beforeClose?: Function,
|
|
21
|
+
afterClose?: Function
|
|
22
|
+
}
|
|
23
|
+
}) -> SheetRef
|
|
24
|
+
|
|
25
|
+
// SheetRef object
|
|
26
|
+
{
|
|
27
|
+
close(): Promise<void>, // Programmatically close the sheet
|
|
28
|
+
id: string, // Unique ID of the sheet
|
|
29
|
+
element: HTMLElement // The sheet element
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Sheet Component
|
|
33
|
+
<schmancy-sheet
|
|
34
|
+
title="Sheet Title"
|
|
35
|
+
position="right|left|bottom|top"
|
|
36
|
+
width="600px"
|
|
37
|
+
height="auto"
|
|
38
|
+
closable?
|
|
39
|
+
backdrop?
|
|
40
|
+
@close=${handleClose}>
|
|
41
|
+
|
|
42
|
+
<div slot="header">
|
|
43
|
+
<!-- Custom header content -->
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<!-- Main content -->
|
|
47
|
+
<div>Sheet content goes here</div>
|
|
48
|
+
|
|
49
|
+
<div slot="footer">
|
|
50
|
+
<!-- Footer content -->
|
|
51
|
+
<schmancy-button @click=${closeSheet}>Close</schmancy-button>
|
|
52
|
+
</div>
|
|
53
|
+
</schmancy-sheet>
|
|
54
|
+
|
|
55
|
+
// Sheet Header Component
|
|
56
|
+
<schmancy-sheet-header
|
|
57
|
+
title="Sheet Title"
|
|
58
|
+
closable?
|
|
59
|
+
@close=${handleClose}>
|
|
60
|
+
<!-- Optional additional header content -->
|
|
61
|
+
</schmancy-sheet-header>
|
|
62
|
+
|
|
63
|
+
// Sheet Hook (for programmatic usage)
|
|
64
|
+
const [openSheet, SheetComponent] = useSheet({
|
|
65
|
+
title: "Sheet Title",
|
|
66
|
+
content: () => html`<div>Sheet content</div>`,
|
|
67
|
+
position: "right"
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Examples
|
|
71
|
+
// Basic usage
|
|
72
|
+
$sheet.open({
|
|
73
|
+
title: "User Details",
|
|
74
|
+
content: html`
|
|
75
|
+
<div>
|
|
76
|
+
<schmancy-form>
|
|
77
|
+
<schmancy-input label="Name" value="John Doe"></schmancy-input>
|
|
78
|
+
<schmancy-input label="Email" value="john@example.com"></schmancy-input>
|
|
79
|
+
</schmancy-form>
|
|
80
|
+
</div>
|
|
81
|
+
`,
|
|
82
|
+
onClose: () => console.log("Sheet closed")
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Using with form submission
|
|
86
|
+
const sheetRef = $sheet.open({
|
|
87
|
+
title: "Add User",
|
|
88
|
+
content: html`
|
|
89
|
+
<schmancy-form @submit=${(e) => {
|
|
90
|
+
const values = e.detail.values;
|
|
91
|
+
saveUser(values);
|
|
92
|
+
sheetRef.close();
|
|
93
|
+
}}>
|
|
94
|
+
<schmancy-input name="name" label="Name" required></schmancy-input>
|
|
95
|
+
<schmancy-input name="email" label="Email" required></schmancy-input>
|
|
96
|
+
|
|
97
|
+
<div slot="footer">
|
|
98
|
+
<schmancy-button type="submit">Save</schmancy-button>
|
|
99
|
+
<schmancy-button kind="secondary" @click=${() => sheetRef.close()}>Cancel</schmancy-button>
|
|
100
|
+
</div>
|
|
101
|
+
</schmancy-form>
|
|
102
|
+
`
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Using the sheet component directly
|
|
106
|
+
<schmancy-sheet
|
|
107
|
+
title="Settings"
|
|
108
|
+
position="right"
|
|
109
|
+
width="400px"
|
|
110
|
+
@close=${() => this.sheetOpen = false}>
|
|
111
|
+
|
|
112
|
+
<div>
|
|
113
|
+
<h3>Preferences</h3>
|
|
114
|
+
<schmancy-checkbox label="Enable notifications"></schmancy-checkbox>
|
|
115
|
+
<schmancy-checkbox label="Dark mode"></schmancy-checkbox>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div slot="footer">
|
|
119
|
+
<schmancy-button @click=${saveSettings}>Save</schmancy-button>
|
|
120
|
+
</div>
|
|
121
|
+
</schmancy-sheet>
|
|
122
|
+
```
|
package/ai/store.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Schmancy Store - AI Reference
|
|
2
|
+
|
|
3
|
+
```js
|
|
4
|
+
// Store Creation
|
|
5
|
+
import { createStore } from 'schmancy/store';
|
|
6
|
+
|
|
7
|
+
// Basic store creation
|
|
8
|
+
const store = createStore({
|
|
9
|
+
initialState: {
|
|
10
|
+
count: 0,
|
|
11
|
+
users: [],
|
|
12
|
+
settings: { darkMode: false }
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Store with actions
|
|
17
|
+
const counterStore = createStore({
|
|
18
|
+
initialState: { count: 0 },
|
|
19
|
+
actions: {
|
|
20
|
+
increment: (state, amount = 1) => { state.count += amount; },
|
|
21
|
+
decrement: (state, amount = 1) => { state.count -= amount; },
|
|
22
|
+
reset: (state) => { state.count = 0; }
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Store Context Creation
|
|
27
|
+
import { createContext, createCollectionContext } from 'schmancy/store';
|
|
28
|
+
|
|
29
|
+
// Object/array/collection contexts
|
|
30
|
+
const settingsContext = createContext(store, 'settings'); // Object
|
|
31
|
+
const usersContext = createContext(store, 'users'); // Array
|
|
32
|
+
const todoCollection = createCollectionContext(store, 'todos'); // Collection with IDs
|
|
33
|
+
|
|
34
|
+
// State Manipulation Methods
|
|
35
|
+
store.createAction('addUser', (state, user) => { // Custom action
|
|
36
|
+
state.users.push(user);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
store.update(state => { // Direct update
|
|
40
|
+
state.count++;
|
|
41
|
+
state.settings.darkMode = true;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
todoCollection.add({ id: '1', text: 'Buy milk' }); // Collection add
|
|
45
|
+
todoCollection.update('1', item => { item.completed = true; }); // Collection update
|
|
46
|
+
todoCollection.remove('1'); // Collection remove
|
|
47
|
+
|
|
48
|
+
// Selectors
|
|
49
|
+
import { createSelector, useSelector } from 'schmancy/store';
|
|
50
|
+
|
|
51
|
+
// Basic and computed selectors
|
|
52
|
+
const getCount = state => state.count;
|
|
53
|
+
const getActiveUsers = createSelector(
|
|
54
|
+
state => state.users,
|
|
55
|
+
(users) => users.filter(user => user.active)
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
// Component integration
|
|
59
|
+
function MyComponent() {
|
|
60
|
+
const count = useSelector(getCount);
|
|
61
|
+
const activeUsers = useSelector(getActiveUsers);
|
|
62
|
+
|
|
63
|
+
// React to state changes
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Examples
|
|
67
|
+
// 1. Theme store with persistence
|
|
68
|
+
const themeStore = createStore({
|
|
69
|
+
initialState: { mode: 'light', fontSize: 'medium' },
|
|
70
|
+
actions: {
|
|
71
|
+
toggleTheme: state => {
|
|
72
|
+
state.mode = state.mode === 'light' ? 'dark' : 'light';
|
|
73
|
+
},
|
|
74
|
+
setFontSize: (state, size) => {
|
|
75
|
+
state.fontSize = size;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// LocalStorage persistence
|
|
81
|
+
import { StorageManager } from 'schmancy/store';
|
|
82
|
+
new StorageManager({
|
|
83
|
+
key: 'theme-store',
|
|
84
|
+
storage: localStorage
|
|
85
|
+
}).connect(themeStore);
|
|
86
|
+
|
|
87
|
+
// 2. Todo application store
|
|
88
|
+
const todoStore = createStore({
|
|
89
|
+
initialState: {
|
|
90
|
+
todos: [],
|
|
91
|
+
filter: 'all'
|
|
92
|
+
},
|
|
93
|
+
actions: {
|
|
94
|
+
addTodo: (state, text) => {
|
|
95
|
+
state.todos.push({
|
|
96
|
+
id: Date.now().toString(),
|
|
97
|
+
text,
|
|
98
|
+
completed: false
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
toggleTodo: (state, id) => {
|
|
102
|
+
const todo = state.todos.find(todo => todo.id === id);
|
|
103
|
+
if (todo) todo.completed = !todo.completed;
|
|
104
|
+
},
|
|
105
|
+
setFilter: (state, filter) => {
|
|
106
|
+
state.filter = filter;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// 3. Filtered todos selector
|
|
112
|
+
const getFilteredTodos = createSelector(
|
|
113
|
+
state => state.todos,
|
|
114
|
+
state => state.filter,
|
|
115
|
+
(todos, filter) => {
|
|
116
|
+
switch (filter) {
|
|
117
|
+
case 'active': return todos.filter(t => !t.completed);
|
|
118
|
+
case 'completed': return todos.filter(t => t.completed);
|
|
119
|
+
default: return todos;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Related Components
|
|
126
|
+
- **[Area](./area.md)**: Uses similar reactive patterns for routing state
|
|
127
|
+
- **[Theme](./theme.md)**: Often uses store for theme state management
|
|
128
|
+
- **[Form](./form.md)**: Can use store for form state management
|
|
129
|
+
|
|
130
|
+
## Technical Details
|
|
131
|
+
|
|
132
|
+
### Store Interfaces
|
|
133
|
+
```typescript
|
|
134
|
+
interface StoreOptions<T> {
|
|
135
|
+
initialState: T;
|
|
136
|
+
actions?: Record<string, (state: T, ...args: any[]) => void>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
interface Store<T> {
|
|
140
|
+
getState(): T;
|
|
141
|
+
update(updater: (state: T) => void): void;
|
|
142
|
+
subscribe(listener: (state: T) => void): () => void;
|
|
143
|
+
createAction<Args extends any[]>(
|
|
144
|
+
name: string,
|
|
145
|
+
action: (state: T, ...args: Args) => void
|
|
146
|
+
): (...args: Args) => void;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface Context<T> {
|
|
150
|
+
get(): T;
|
|
151
|
+
update(updater: (state: T) => void): void;
|
|
152
|
+
subscribe(listener: (state: T) => void): () => void;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
interface CollectionContext<T> extends Context<Record<string, T>> {
|
|
156
|
+
add(item: T): void;
|
|
157
|
+
update(id: string, updater: (item: T) => void): void;
|
|
158
|
+
remove(id: string): void;
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Integration with Immer
|
|
163
|
+
The store uses Immer under the hood to enable immutable updates with mutable syntax. This means you can write code that appears to directly modify the state, but behind the scenes, it's creating a new immutable state tree.
|
|
164
|
+
|
|
165
|
+
```js
|
|
166
|
+
// This looks like direct mutation but is actually immutable
|
|
167
|
+
store.update(state => {
|
|
168
|
+
state.count++; // Modifying a primitive
|
|
169
|
+
state.users.push(user); // Modifying an array
|
|
170
|
+
state.deep.nested.value = true; // Modifying a nested property
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Common Use Cases
|
|
175
|
+
|
|
176
|
+
1. **Application theme state**
|
|
177
|
+
```js
|
|
178
|
+
const themeStore = createStore({
|
|
179
|
+
initialState: { mode: 'light' },
|
|
180
|
+
actions: {
|
|
181
|
+
toggleTheme: state => {
|
|
182
|
+
state.mode = state.mode === 'light' ? 'dark' : 'light';
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// In a component
|
|
188
|
+
const mode = useSelector(state => state.mode);
|
|
189
|
+
const toggleTheme = () => themeStore.actions.toggleTheme();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
2. **Form state management**
|
|
193
|
+
```js
|
|
194
|
+
const formStore = createStore({
|
|
195
|
+
initialState: {
|
|
196
|
+
values: { name: '', email: '' },
|
|
197
|
+
errors: {},
|
|
198
|
+
isDirty: false,
|
|
199
|
+
isSubmitting: false
|
|
200
|
+
},
|
|
201
|
+
actions: {
|
|
202
|
+
setField: (state, field, value) => {
|
|
203
|
+
state.values[field] = value;
|
|
204
|
+
state.isDirty = true;
|
|
205
|
+
},
|
|
206
|
+
setError: (state, field, error) => {
|
|
207
|
+
state.errors[field] = error;
|
|
208
|
+
},
|
|
209
|
+
submit: (state) => {
|
|
210
|
+
state.isSubmitting = true;
|
|
211
|
+
},
|
|
212
|
+
reset: (state) => {
|
|
213
|
+
state.values = { name: '', email: '' };
|
|
214
|
+
state.errors = {};
|
|
215
|
+
state.isDirty = false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
3. **Filtered data with memoization**
|
|
222
|
+
```js
|
|
223
|
+
const getFilteredItems = createSelector(
|
|
224
|
+
state => state.items,
|
|
225
|
+
state => state.filters,
|
|
226
|
+
(items, filters) => {
|
|
227
|
+
return items.filter(item => {
|
|
228
|
+
// Apply filters
|
|
229
|
+
return Object.entries(filters).every(([key, value]) =>
|
|
230
|
+
!value || item[key] === value
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
```
|