flet-box 0.0.0-stage → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +190 -2
- package/bin/cli.js +394 -0
- package/bin/commands/buildAndroid.js +200 -0
- package/bin/commands/buildIOS.js +237 -0
- package/bin/commands/create.js +406 -0
- package/bin/commands/createBundle.js +375 -0
- package/bin/commands/createComponent.js +115 -0
- package/bin/commands/createScreen.js +161 -0
- package/bin/commands/killServer.js +107 -0
- package/bin/commands/package.js +131 -0
- package/bin/commands/release.js +189 -0
- package/bin/commands/runBundle.js +62 -0
- package/bin/commands/runCloudflare.js +89 -0
- package/bin/commands/runNative.js +109 -0
- package/bin/commands/runServer.js +390 -0
- package/bin/ota/loader.js +361 -0
- package/bin/utils/bundleConfig.js +178 -0
- package/bin/utils/capacitor.js +137 -0
- package/bin/utils/colors.js +215 -0
- package/bin/utils/helpers.js +80 -0
- package/bin/utils/spinner.js +96 -0
- package/bin/utils/templates.js +1824 -0
- package/bin/utils/version.js +108 -0
- package/package.json +58 -4
- package/src/animations/AnimatedBox.js +607 -0
- package/src/animations/AnimatedText.js +105 -0
- package/src/animations/MatrixRain.js +220 -0
- package/src/animations/ParallaxBox.js +369 -0
- package/src/animations/animate.js +332 -0
- package/src/animations/index.js +25 -0
- package/src/app.js +41 -0
- package/src/assets/icon-1024-maskable.png +0 -0
- package/src/assets/icon-192-maskable.png +0 -0
- package/src/assets/icon-192.png +0 -0
- package/src/assets/icon-512-maskable.png +0 -0
- package/src/assets/icon-512.png +0 -0
- package/src/assets/logo.png +0 -0
- package/src/core/App.js +44 -0
- package/src/core/hmr-client.js +142 -0
- package/src/core/index.js +34 -0
- package/src/core/pwa.js +100 -0
- package/src/core/runApp.js +430 -0
- package/src/create-index.sh +237 -0
- package/src/fonts/MaterialIcons-Regular.woff2 +0 -0
- package/src/fonts/icons.css +23 -0
- package/src/index.d.ts +1930 -0
- package/src/index.js +288 -0
- package/src/navigations/AdaptiveScaffold.js +149 -0
- package/src/navigations/AppBar.js +331 -0
- package/src/navigations/BottomNavigation.js +298 -0
- package/src/navigations/CollapsibleSideBar.js +139 -0
- package/src/navigations/Drawer.js +333 -0
- package/src/navigations/DrawerItem.js +260 -0
- package/src/navigations/Router.js +475 -0
- package/src/navigations/Scaffold.js +537 -0
- package/src/navigations/Tabs.js +384 -0
- package/src/navigations/index.js +57 -0
- package/src/services/RamStore.js +213 -0
- package/src/services/Session.js +310 -0
- package/src/services/Storage.js +309 -0
- package/src/services/http.js +350 -0
- package/src/services/index.js +57 -0
- package/src/tools/animation.js +42 -0
- package/src/tools/array.js +95 -0
- package/src/tools/border.js +28 -0
- package/src/tools/clipboard.js +104 -0
- package/src/tools/color.js +329 -0
- package/src/tools/createList.js +215 -0
- package/src/tools/delay.js +55 -0
- package/src/tools/device.js +138 -0
- package/src/tools/dict.js +297 -0
- package/src/tools/dimensions.js +118 -0
- package/src/tools/filter.js +41 -0
- package/src/tools/flex.js +26 -0
- package/src/tools/gradient.js +34 -0
- package/src/tools/grid.js +57 -0
- package/src/tools/index.js +38 -0
- package/src/tools/mapList.js +107 -0
- package/src/tools/margin.js +93 -0
- package/src/tools/memo.js +60 -0
- package/src/tools/os.js +104 -0
- package/src/tools/padding.js +93 -0
- package/src/tools/print.js +69 -0
- package/src/tools/random.js +299 -0
- package/src/tools/ref.js +70 -0
- package/src/tools/rgba.js +4 -0
- package/src/tools/shadow.js +23 -0
- package/src/tools/string.js +85 -0
- package/src/tools/time.js +674 -0
- package/src/tools/transform.js +68 -0
- package/src/tools/transition.js +48 -0
- package/src/tools/useState.js +268 -0
- package/src/tools/uuid.js +42 -0
- package/src/utils/TextInputValidator.js +256 -0
- package/src/utils/composeUpdate.js +65 -0
- package/src/utils/getWidgetProps.js +35 -0
- package/src/utils/index.js +76 -0
- package/src/utils/markdownParser.js +368 -0
- package/src/utils/mediaTime.js +79 -0
- package/src/utils/navigation.js +263 -0
- package/src/utils/scriptInjector.js +73 -0
- package/src/utils/stackPosition.js +31 -0
- package/src/utils/stopWebRefresh.js +77 -0
- package/src/utils/styleInjector.js +38 -0
- package/src/utils/syntaxHighlight.js +339 -0
- package/src/utils/themes.js +347 -0
- package/src/utils/units.js +87 -0
- package/src/utils/visualEffects.js +169 -0
- package/src/widget-factory/addChildren.js +114 -0
- package/src/widget-factory/assignProps.js +256 -0
- package/src/widget-factory/createWidget.js +28 -0
- package/src/widget-factory/effects.js +132 -0
- package/src/widget-factory/index.js +7 -0
- package/src/widget-factory/lifecycle.js +210 -0
- package/src/widget-factory/makeParentable.js +89 -0
- package/src/widget-factory/pixTool.js +40 -0
- package/src/widget-factory/processProps.js +122 -0
- package/src/widget-factory/reactivity.js +98 -0
- package/src/widget-factory/remTool.js +34 -0
- package/src/widget-factory/stackPosition.js +87 -0
- package/src/widget-factory/stringTool.js +26 -0
- package/src/widget-factory/tools.js +249 -0
- package/src/widget-factory/translateProps.js +1049 -0
- package/src/widget-factory/warnings.js +284 -0
- package/src/widget-factory/widgetFactory.js +200 -0
- package/src/widgets/Accordion.js +562 -0
- package/src/widgets/AdMob.js +315 -0
- package/src/widgets/AdSense.js +268 -0
- package/src/widgets/AlertDialog.js +162 -0
- package/src/widgets/Audio.js +311 -0
- package/src/widgets/Avatar.js +210 -0
- package/src/widgets/Badge.js +189 -0
- package/src/widgets/BottomSheet.js +287 -0
- package/src/widgets/Button.js +247 -0
- package/src/widgets/Card.js +33 -0
- package/src/widgets/Carousel.js +301 -0
- package/src/widgets/Chart.js +632 -0
- package/src/widgets/Checkbox.js +106 -0
- package/src/widgets/Chip.js +153 -0
- package/src/widgets/CircularBar.js +352 -0
- package/src/widgets/CircularChart.js +549 -0
- package/src/widgets/CodeViewer.js +236 -0
- package/src/widgets/Column.js +29 -0
- package/src/widgets/Container.js +31 -0
- package/src/widgets/DataTable.js +257 -0
- package/src/widgets/Divider.js +93 -0
- package/src/widgets/DraggBox.js +115 -0
- package/src/widgets/Dropdown.js +505 -0
- package/src/widgets/DroppBox.js +180 -0
- package/src/widgets/FloatingActionButton.js +91 -0
- package/src/widgets/GridView.js +20 -0
- package/src/widgets/Icon.js +33 -0
- package/src/widgets/Image.js +15 -0
- package/src/widgets/Input.js +565 -0
- package/src/widgets/Inspector.js +254 -0
- package/src/widgets/InstallButton.js +110 -0
- package/src/widgets/ListTile.js +224 -0
- package/src/widgets/ListView.js +503 -0
- package/src/widgets/Markdown.js +1236 -0
- package/src/widgets/Modal.js +496 -0
- package/src/widgets/Pagination.js +195 -0
- package/src/widgets/ProgressBar.js +178 -0
- package/src/widgets/QRCode.js +467 -0
- package/src/widgets/Radio.js +132 -0
- package/src/widgets/Rating.js +261 -0
- package/src/widgets/Row.js +29 -0
- package/src/widgets/Skeleton.js +243 -0
- package/src/widgets/Slider.js +527 -0
- package/src/widgets/SnackBar.js +227 -0
- package/src/widgets/Stack.js +29 -0
- package/src/widgets/Stepper.js +296 -0
- package/src/widgets/Switch.js +102 -0
- package/src/widgets/Text.js +98 -0
- package/src/widgets/Tooltip.js +267 -0
- package/src/widgets/TreeView.js +370 -0
- package/src/widgets/Video.js +92 -0
- package/src/widgets/index.js +52 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
// FletBox/services/http.js
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* HTTP requests module - Python requests style
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // GET
|
|
8
|
+
* const users = await httpGet('https://api.example.com/users');
|
|
9
|
+
*
|
|
10
|
+
* // POST
|
|
11
|
+
* const newUser = await httpPost('https://api.example.com/users', {
|
|
12
|
+
* body: { name: 'Juan', email: 'juan@email.com' }
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* // PUT
|
|
16
|
+
* const updated = await httpPut('https://api.example.com/users/1', {
|
|
17
|
+
* body: { name: 'Juan Carlos' }
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // DELETE
|
|
21
|
+
* await httpDelete('https://api.example.com/users/1');
|
|
22
|
+
*
|
|
23
|
+
* // With headers
|
|
24
|
+
* const data = await httpGet('https://api.example.com/protected', {
|
|
25
|
+
* headers: { 'Authorization': 'Bearer token123' }
|
|
26
|
+
* });
|
|
27
|
+
* // FletBox/services/http.js
|
|
28
|
+
*
|
|
29
|
+
* HTTP requests module - Python requests style
|
|
30
|
+
*
|
|
31
|
+
* @description
|
|
32
|
+
* Module for making HTTP requests with a simple and consistent syntax.
|
|
33
|
+
* Supports GET, POST, PUT, PATCH, DELETE with timeout, headers, and params.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // 1. GET - Fetch data on start
|
|
37
|
+
* import { useEffect, useState } from 'react';
|
|
38
|
+
* import { Container, Text, httpGet } from 'fletbox';
|
|
39
|
+
*
|
|
40
|
+
* function UsersScreen() {
|
|
41
|
+
* const [users, setUsers] = useState([]);
|
|
42
|
+
*
|
|
43
|
+
* useEffect(() => {
|
|
44
|
+
* async function loadUsers() {
|
|
45
|
+
* const data = await httpGet('https://jsonplaceholder.typicode.com/users');
|
|
46
|
+
* setUsers(data);
|
|
47
|
+
* }
|
|
48
|
+
* loadUsers();
|
|
49
|
+
* }, []);
|
|
50
|
+
*
|
|
51
|
+
* return (
|
|
52
|
+
* <Container>
|
|
53
|
+
* <Text>Total users: {users.length}</Text>
|
|
54
|
+
* </Container>
|
|
55
|
+
* );
|
|
56
|
+
* }
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // 2. POST - Create data
|
|
60
|
+
* import { Button, Container, Text, httpPost } from 'fletbox';
|
|
61
|
+
*
|
|
62
|
+
* function CreateUserScreen() {
|
|
63
|
+
* const [result, setResult] = useState(null);
|
|
64
|
+
*
|
|
65
|
+
* const createUser = async () => {
|
|
66
|
+
* const newUser = await httpPost('https://jsonplaceholder.typicode.com/users', {
|
|
67
|
+
* body: { name: 'Juan', email: 'juan@email.com' }
|
|
68
|
+
* });
|
|
69
|
+
* setResult(newUser);
|
|
70
|
+
* };
|
|
71
|
+
*
|
|
72
|
+
* return (
|
|
73
|
+
* <Container>
|
|
74
|
+
* <Button label="Create User" onPress={createUser} />
|
|
75
|
+
* {result && <Text>User created ID: {result.id}</Text>}
|
|
76
|
+
* </Container>
|
|
77
|
+
* );
|
|
78
|
+
* }
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* // 3. PUT - Update full data
|
|
82
|
+
* import { Button, Container, Text, httpPut } from 'fletbox';
|
|
83
|
+
*
|
|
84
|
+
* function UpdateUserScreen() {
|
|
85
|
+
* const [result, setResult] = useState(null);
|
|
86
|
+
*
|
|
87
|
+
* const updateUser = async () => {
|
|
88
|
+
* const updated = await httpPut('https://jsonplaceholder.typicode.com/users/1', {
|
|
89
|
+
* body: { name: 'Carlos', email: 'carlos@email.com' }
|
|
90
|
+
* });
|
|
91
|
+
* setResult(updated);
|
|
92
|
+
* };
|
|
93
|
+
*
|
|
94
|
+
* return (
|
|
95
|
+
* <Container>
|
|
96
|
+
* <Button label="Update User" onPress={updateUser} />
|
|
97
|
+
* {result && <Text>User updated: {result.name}</Text>}
|
|
98
|
+
* </Container>
|
|
99
|
+
* );
|
|
100
|
+
* }
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* // 4. PATCH - Update partial data
|
|
104
|
+
* import { Button, Container, Text, httpPatch } from 'fletbox';
|
|
105
|
+
*
|
|
106
|
+
* function PatchUserScreen() {
|
|
107
|
+
* const [result, setResult] = useState(null);
|
|
108
|
+
*
|
|
109
|
+
* const patchUser = async () => {
|
|
110
|
+
* const patched = await httpPatch('https://jsonplaceholder.typicode.com/users/1', {
|
|
111
|
+
* body: { email: 'nuevo@email.com' }
|
|
112
|
+
* });
|
|
113
|
+
* setResult(patched);
|
|
114
|
+
* };
|
|
115
|
+
*
|
|
116
|
+
* return (
|
|
117
|
+
* <Container>
|
|
118
|
+
* <Button label="Update Email" onPress={patchUser} />
|
|
119
|
+
* {result && <Text>Email updated: {result.email}</Text>}
|
|
120
|
+
* </Container>
|
|
121
|
+
* );
|
|
122
|
+
* }
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* // 5. DELETE - Delete data
|
|
126
|
+
* import { Button, Container, Text, httpDelete } from 'fletbox';
|
|
127
|
+
*
|
|
128
|
+
* function DeleteUserScreen() {
|
|
129
|
+
* const [deleted, setDeleted] = useState(false);
|
|
130
|
+
*
|
|
131
|
+
* const deleteUser = async () => {
|
|
132
|
+
* await httpDelete('https://jsonplaceholder.typicode.com/users/1');
|
|
133
|
+
* setDeleted(true);
|
|
134
|
+
* };
|
|
135
|
+
*
|
|
136
|
+
* return (
|
|
137
|
+
* <Container>
|
|
138
|
+
* <Button label="Delete User" onPress={deleteUser} />
|
|
139
|
+
* {deleted && <Text>User deleted</Text>}
|
|
140
|
+
* </Container>
|
|
141
|
+
* );
|
|
142
|
+
* }
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* // 6. GET with headers and params
|
|
146
|
+
* import { useEffect, useState } from 'react';
|
|
147
|
+
* import { Container, Text, httpGet } from 'fletbox';
|
|
148
|
+
*
|
|
149
|
+
* function ProfileScreen() {
|
|
150
|
+
* const [profile, setProfile] = useState(null);
|
|
151
|
+
*
|
|
152
|
+
* useEffect(() => {
|
|
153
|
+
* async function loadProfile() {
|
|
154
|
+
* const data = await httpGet('https://api.example.com/profile', {
|
|
155
|
+
* headers: { 'Authorization': 'Bearer token123' },
|
|
156
|
+
* params: { userId: 1 }
|
|
157
|
+
* });
|
|
158
|
+
* setProfile(data);
|
|
159
|
+
* }
|
|
160
|
+
* loadProfile();
|
|
161
|
+
* }, []);
|
|
162
|
+
*
|
|
163
|
+
* return (
|
|
164
|
+
* <Container>
|
|
165
|
+
* <Text>Name: {profile?.name}</Text>
|
|
166
|
+
* <Text>Email: {profile?.email}</Text>
|
|
167
|
+
* </Container>
|
|
168
|
+
* );
|
|
169
|
+
* }
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* // 7. Error handling
|
|
173
|
+
* import { useEffect, useState } from 'react';
|
|
174
|
+
* import { Container, Text, httpGet } from 'fletbox';
|
|
175
|
+
*
|
|
176
|
+
* function ErrorScreen() {
|
|
177
|
+
* const [data, setData] = useState(null);
|
|
178
|
+
* const [error, setError] = useState(null);
|
|
179
|
+
*
|
|
180
|
+
* useEffect(() => {
|
|
181
|
+
* async function loadData() {
|
|
182
|
+
* try {
|
|
183
|
+
* const result = await httpGet('https://api.example.com/data');
|
|
184
|
+
* setData(result);
|
|
185
|
+
* } catch (err) {
|
|
186
|
+
* setError(err.message);
|
|
187
|
+
* }
|
|
188
|
+
* }
|
|
189
|
+
* loadData();
|
|
190
|
+
* }, []);
|
|
191
|
+
*
|
|
192
|
+
* return (
|
|
193
|
+
* <Container>
|
|
194
|
+
* {error && <Text color="red">Error: {error}</Text>}
|
|
195
|
+
* {data && <Text>Data loaded</Text>}
|
|
196
|
+
* </Container>
|
|
197
|
+
* );
|
|
198
|
+
* }
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* // 8. Loading state
|
|
202
|
+
* import { useEffect, useState } from 'react';
|
|
203
|
+
* import { Container, Text, ActivityIndicator, httpGet } from 'fletbox';
|
|
204
|
+
*
|
|
205
|
+
* function LoadingScreen() {
|
|
206
|
+
* const [users, setUsers] = useState([]);
|
|
207
|
+
* const [loading, setLoading] = useState(true);
|
|
208
|
+
*
|
|
209
|
+
* useEffect(() => {
|
|
210
|
+
* async function loadUsers() {
|
|
211
|
+
* const data = await httpGet('https://jsonplaceholder.typicode.com/users');
|
|
212
|
+
* setUsers(data);
|
|
213
|
+
* setLoading(false);
|
|
214
|
+
* }
|
|
215
|
+
* loadUsers();
|
|
216
|
+
* }, []);
|
|
217
|
+
*
|
|
218
|
+
* return (
|
|
219
|
+
* <Container>
|
|
220
|
+
* {loading ? (
|
|
221
|
+
* <ActivityIndicator size="large" />
|
|
222
|
+
* ) : (
|
|
223
|
+
* <Text>Users loaded: {users.length}</Text>
|
|
224
|
+
* )}
|
|
225
|
+
* </Container>
|
|
226
|
+
* );
|
|
227
|
+
* }
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* // 9. POST with loading
|
|
231
|
+
* import { useState } from 'react';
|
|
232
|
+
* import { Button, Container, Text, ActivityIndicator, httpPost } from 'fletbox';
|
|
233
|
+
*
|
|
234
|
+
* function SubmitScreen() {
|
|
235
|
+
* const [loading, setLoading] = useState(false);
|
|
236
|
+
* const [result, setResult] = useState(null);
|
|
237
|
+
*
|
|
238
|
+
* const submit = async () => {
|
|
239
|
+
* setLoading(true);
|
|
240
|
+
* try {
|
|
241
|
+
* const data = await httpPost('https://api.example.com/submit', {
|
|
242
|
+
* body: { message: 'Hello world' }
|
|
243
|
+
* });
|
|
244
|
+
* setResult(data);
|
|
245
|
+
* } finally {
|
|
246
|
+
* setLoading(false);
|
|
247
|
+
* }
|
|
248
|
+
* };
|
|
249
|
+
*
|
|
250
|
+
* return (
|
|
251
|
+
* <Container>
|
|
252
|
+
* {loading ? (
|
|
253
|
+
* <ActivityIndicator size="large" />
|
|
254
|
+
* ) : (
|
|
255
|
+
* <Button label="Submit" onPress={submit} />
|
|
256
|
+
* )}
|
|
257
|
+
* {result && <Text>Submitted: {result.id}</Text>}
|
|
258
|
+
* </Container>
|
|
259
|
+
* );
|
|
260
|
+
* }
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* // 10. Custom timeout
|
|
264
|
+
* import { httpGet } from 'fletbox';
|
|
265
|
+
*
|
|
266
|
+
* async function slowRequest() {
|
|
267
|
+
* try {
|
|
268
|
+
* const data = await httpGet('https://api.lenta.com/datos', {
|
|
269
|
+
* timeout: 5000 // 5 seconds max
|
|
270
|
+
* });
|
|
271
|
+
* console.log(data);
|
|
272
|
+
* } catch (error) {
|
|
273
|
+
* console.log('Timeout or error:', error.message);
|
|
274
|
+
* }
|
|
275
|
+
* }
|
|
276
|
+
*/
|
|
277
|
+
|
|
278
|
+
async function httpRequest(method, url, options = {}) {
|
|
279
|
+
const { body, headers = {}, params = {}, timeout = 30000, ...rest } = options;
|
|
280
|
+
|
|
281
|
+
const queryString = new URLSearchParams(
|
|
282
|
+
Object.entries(params).filter(([, value]) => value !== null && value !== undefined),
|
|
283
|
+
).toString();
|
|
284
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
285
|
+
const finalUrl = queryString ? `${url}${separator}${queryString}` : url;
|
|
286
|
+
|
|
287
|
+
const config = {
|
|
288
|
+
method: method.toUpperCase(),
|
|
289
|
+
headers: {
|
|
290
|
+
"Content-Type": "application/json",
|
|
291
|
+
...headers,
|
|
292
|
+
},
|
|
293
|
+
signal: AbortSignal.timeout(timeout),
|
|
294
|
+
...rest,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
if (body !== undefined) {
|
|
298
|
+
config.body = typeof body === "string" ? body : JSON.stringify(body);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
try {
|
|
302
|
+
const response = await fetch(finalUrl, config);
|
|
303
|
+
|
|
304
|
+
let data;
|
|
305
|
+
const contentType = response.headers.get("content-type");
|
|
306
|
+
if (contentType?.includes("application/json")) {
|
|
307
|
+
data = await response.json();
|
|
308
|
+
} else {
|
|
309
|
+
data = await response.text();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (!response.ok) {
|
|
313
|
+
const error = new Error(
|
|
314
|
+
`HTTP ${response.status}: ${response.statusText}`,
|
|
315
|
+
);
|
|
316
|
+
error.response = response;
|
|
317
|
+
error.data = data;
|
|
318
|
+
throw error;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return data;
|
|
322
|
+
} catch (error) {
|
|
323
|
+
if (error.name === "TimeoutError") {
|
|
324
|
+
throw new Error(`Request timeout after ${timeout}ms`);
|
|
325
|
+
}
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export async function httpGet(url, options = {}) {
|
|
331
|
+
return httpRequest("GET", url, options);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
export async function httpPost(url, options = {}) {
|
|
335
|
+
return httpRequest("POST", url, options);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export async function httpPut(url, options = {}) {
|
|
339
|
+
return httpRequest("PUT", url, options);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export async function httpPatch(url, options = {}) {
|
|
343
|
+
return httpRequest("PATCH", url, options);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export async function httpDelete(url, options = {}) {
|
|
347
|
+
return httpRequest("DELETE", url, options);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
export { httpRequest };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// src/services/index.js
|
|
2
|
+
export {
|
|
3
|
+
default as RamStore,
|
|
4
|
+
saveRam,
|
|
5
|
+
getRam,
|
|
6
|
+
getAllRam,
|
|
7
|
+
getAllRamKeys,
|
|
8
|
+
hasRam,
|
|
9
|
+
updateRam,
|
|
10
|
+
deleteRam,
|
|
11
|
+
clearAllRam,
|
|
12
|
+
subscribeRam,
|
|
13
|
+
getRamItemCount,
|
|
14
|
+
isRamAvailable,
|
|
15
|
+
} from "./RamStore.js";
|
|
16
|
+
export {
|
|
17
|
+
default as Session,
|
|
18
|
+
saveSession,
|
|
19
|
+
getSession,
|
|
20
|
+
getSessionSync,
|
|
21
|
+
updateSession,
|
|
22
|
+
deleteSession,
|
|
23
|
+
clearAllSession,
|
|
24
|
+
hasSession,
|
|
25
|
+
getAllSessionKeys,
|
|
26
|
+
getAllSessionData,
|
|
27
|
+
getSessionSize,
|
|
28
|
+
deleteSessionByPrefix,
|
|
29
|
+
deleteSessionBySuffix,
|
|
30
|
+
getSessionItemCount,
|
|
31
|
+
isSessionAvailable,
|
|
32
|
+
} from "./Session.js";
|
|
33
|
+
export {
|
|
34
|
+
default as Storage,
|
|
35
|
+
saveData,
|
|
36
|
+
getData,
|
|
37
|
+
getDataSync,
|
|
38
|
+
updateData,
|
|
39
|
+
deleteData,
|
|
40
|
+
clearAllData,
|
|
41
|
+
hasData,
|
|
42
|
+
getAllKeys,
|
|
43
|
+
getAllData,
|
|
44
|
+
getStorageSize,
|
|
45
|
+
deleteDataByPrefix,
|
|
46
|
+
deleteDataBySuffix,
|
|
47
|
+
getItemCount,
|
|
48
|
+
isStorageAvailable,
|
|
49
|
+
} from "./Storage.js";
|
|
50
|
+
export {
|
|
51
|
+
httpGet,
|
|
52
|
+
httpPost,
|
|
53
|
+
httpPut,
|
|
54
|
+
httpPatch,
|
|
55
|
+
httpDelete,
|
|
56
|
+
httpRequest,
|
|
57
|
+
} from "./http.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// tools/animation.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate CSS animation string
|
|
5
|
+
* @param {string|Object} name - Animation name or options object
|
|
6
|
+
* @param {number} duration - Duration in seconds
|
|
7
|
+
* @param {string} timing - Timing function
|
|
8
|
+
* @param {string} iteration - Iteration count
|
|
9
|
+
* @returns {string} CSS animation value
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* animation('fadeIn', 0.5, 'ease-in-out', 'infinite')
|
|
13
|
+
* // "fadeIn 0.5s ease-in-out infinite"
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* animation({ name: 'slideIn', duration: 0.3, timing: 'ease', delay: 0.1, fillMode: 'forwards' })
|
|
17
|
+
* // "slideIn 0.3s ease 0.1s forwards"
|
|
18
|
+
*/
|
|
19
|
+
export const animation = (
|
|
20
|
+
name,
|
|
21
|
+
duration = 0.3,
|
|
22
|
+
timing = "ease",
|
|
23
|
+
iteration = "1",
|
|
24
|
+
) => {
|
|
25
|
+
if (typeof name === "object") {
|
|
26
|
+
const n = name.name;
|
|
27
|
+
const d = name.duration ?? 0.3;
|
|
28
|
+
const t = name.timing ?? "ease";
|
|
29
|
+
const dl = name.delay ?? 0;
|
|
30
|
+
const i = name.iteration ?? "1";
|
|
31
|
+
const dir = name.direction ?? "normal";
|
|
32
|
+
const fill = name.fillMode ?? "none";
|
|
33
|
+
|
|
34
|
+
let result = `${n} ${d}s ${t} ${dl}s ${i} ${dir}`;
|
|
35
|
+
if (fill !== "none") result += ` ${fill}`;
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return `${name} ${duration}s ${timing} ${iteration}`;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default animation;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// tools/array.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shuffle an array (Fisher-Yates)
|
|
5
|
+
* @param {Array} arr - Input array
|
|
6
|
+
* @returns {Array} New shuffled array
|
|
7
|
+
* @example shuffle([1,2,3,4,5]) // [3,1,5,2,4]
|
|
8
|
+
*/
|
|
9
|
+
export const shuffle = (arr) => {
|
|
10
|
+
const result = [...arr];
|
|
11
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
12
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
13
|
+
[result[i], result[j]] = [result[j], result[i]];
|
|
14
|
+
}
|
|
15
|
+
return result;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Reverse an array
|
|
20
|
+
* @param {Array} arr - Input array
|
|
21
|
+
* @returns {Array} New reversed array
|
|
22
|
+
* @example reverse([1,2,3,4]) // [4,3,2,1]
|
|
23
|
+
*/
|
|
24
|
+
export const reverse = (arr) => {
|
|
25
|
+
return [...arr].reverse();
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sort array by key or custom function
|
|
30
|
+
* @param {Array} arr - Input array
|
|
31
|
+
* @param {string|Function} by - Key to sort by or sort function
|
|
32
|
+
* @param {string} order - 'asc' or 'desc' (default: 'asc')
|
|
33
|
+
* @returns {Array} New sorted array
|
|
34
|
+
* @example sort(users, 'name')
|
|
35
|
+
* @example sort(users, (a,b) => a.age - b.age)
|
|
36
|
+
*/
|
|
37
|
+
export const sort = (arr, by, order = "asc") => {
|
|
38
|
+
const result = [...arr];
|
|
39
|
+
|
|
40
|
+
if (typeof by === "function") {
|
|
41
|
+
result.sort(by);
|
|
42
|
+
} else if (typeof by === "string") {
|
|
43
|
+
result.sort((a, b) => {
|
|
44
|
+
const aVal = a[by];
|
|
45
|
+
const bVal = b[by];
|
|
46
|
+
if (typeof aVal === "number") {
|
|
47
|
+
return order === "asc" ? aVal - bVal : bVal - aVal;
|
|
48
|
+
}
|
|
49
|
+
const cmp = String(aVal).localeCompare(String(bVal));
|
|
50
|
+
return order === "asc" ? cmp : -cmp;
|
|
51
|
+
});
|
|
52
|
+
} else {
|
|
53
|
+
result.sort();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return order === "desc" && typeof by !== "function"
|
|
57
|
+
? result.reverse()
|
|
58
|
+
: result;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Get unique values from array
|
|
63
|
+
* @param {Array} arr - Input array
|
|
64
|
+
* @param {string} key - Optional key for objects
|
|
65
|
+
* @returns {Array} Array with unique values
|
|
66
|
+
*/
|
|
67
|
+
export const unique = (arr, key = null) => {
|
|
68
|
+
if (key) {
|
|
69
|
+
const seen = new Set();
|
|
70
|
+
return arr.filter((item) => {
|
|
71
|
+
const value = item[key];
|
|
72
|
+
if (seen.has(value)) return false;
|
|
73
|
+
seen.add(value);
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return [...new Set(arr)];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Chunk array into smaller arrays
|
|
82
|
+
* @param {Array} arr - Input array
|
|
83
|
+
* @param {number} size - Chunk size
|
|
84
|
+
* @returns {Array} Array of chunks
|
|
85
|
+
* @example chunk([1,2,3,4,5], 2) // [[1,2], [3,4], [5]]
|
|
86
|
+
*/
|
|
87
|
+
export const chunk = (arr, size) => {
|
|
88
|
+
const result = [];
|
|
89
|
+
for (let i = 0; i < arr.length; i += size) {
|
|
90
|
+
result.push(arr.slice(i, i + size));
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default { shuffle, reverse, sort, unique, chunk };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// tools/border.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate a CSS border shorthand string.
|
|
5
|
+
*
|
|
6
|
+
* Converts individual border parameters into the `border` CSS shorthand so
|
|
7
|
+
* callers don't have to concatenate strings manually. Accepts either a number
|
|
8
|
+
* (auto-suffixed with `"px"`) or a pre-formatted string for `width`.
|
|
9
|
+
*
|
|
10
|
+
* @param {number|string} [width=1] - Border width. Numbers are treated as pixels;
|
|
11
|
+
* strings are used as-is (e.g. `"0.5rem"`, `"thin"`).
|
|
12
|
+
* @param {string} [style="solid"] - Border style — any valid CSS border-style keyword
|
|
13
|
+
* (`"solid"`, `"dashed"`, `"dotted"`, `"double"`, `"none"`, etc.)
|
|
14
|
+
* @param {string} color - Border color — any valid CSS color value.
|
|
15
|
+
* Required; there is no default because a colorless border is invisible.
|
|
16
|
+
* @returns {string} CSS border shorthand value
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* border(1, 'solid', '#ccc') // "1px solid #ccc"
|
|
20
|
+
* border(2, 'dashed', 'red') // "2px dashed red"
|
|
21
|
+
* border('0.5rem', 'dotted', 'var(--outline)') // "0.5rem dotted var(--outline)"
|
|
22
|
+
*/
|
|
23
|
+
export const border = (width = 1, style = "solid", color) => {
|
|
24
|
+
const widthStr = typeof width === "number" ? `${width}px` : width;
|
|
25
|
+
return `${widthStr} ${style} ${color}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default border;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// src/components/flet-box/tools/clipboard.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Clipboard utility — provides a synchronous-feeling API for reading from and
|
|
5
|
+
* writing to the system clipboard.
|
|
6
|
+
*
|
|
7
|
+
* `copy` and `copyWithFeedback` are intentionally synchronous from the caller's
|
|
8
|
+
* perspective: the underlying async Clipboard API is handled internally so
|
|
9
|
+
* callers do not need to `await` them. `read` is genuinely async and must be
|
|
10
|
+
* awaited.
|
|
11
|
+
*/
|
|
12
|
+
export const clipboard = {
|
|
13
|
+
/**
|
|
14
|
+
* Copies text to the clipboard.
|
|
15
|
+
*
|
|
16
|
+
* Uses the modern async `navigator.clipboard.writeText` API when available,
|
|
17
|
+
* falling back to a hidden `<textarea>` + `execCommand("copy")` for older
|
|
18
|
+
* browsers. Errors from the async path are caught and silently rerouted to
|
|
19
|
+
* the fallback.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} text - Text to copy.
|
|
22
|
+
* @returns {void}
|
|
23
|
+
*/
|
|
24
|
+
copy: (text) => {
|
|
25
|
+
// Modern path — async internally but fire-and-forget for the caller
|
|
26
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
27
|
+
navigator.clipboard.writeText(text).catch((err) => {
|
|
28
|
+
console.error("Clipboard failed:", err);
|
|
29
|
+
clipboard._fallbackCopy(text);
|
|
30
|
+
});
|
|
31
|
+
} else {
|
|
32
|
+
// Fallback for browsers that do not support the Clipboard API
|
|
33
|
+
clipboard._fallbackCopy(text);
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Legacy synchronous fallback that copies text using a hidden `<textarea>`
|
|
39
|
+
* and the deprecated `execCommand("copy")`.
|
|
40
|
+
*
|
|
41
|
+
* @private
|
|
42
|
+
* @param {string} text - Text to copy.
|
|
43
|
+
* @returns {void}
|
|
44
|
+
*/
|
|
45
|
+
_fallbackCopy: (text) => {
|
|
46
|
+
const textarea = document.createElement("textarea");
|
|
47
|
+
textarea.value = text;
|
|
48
|
+
textarea.style.position = "fixed";
|
|
49
|
+
textarea.style.left = "-9999px";
|
|
50
|
+
textarea.style.top = "-9999px";
|
|
51
|
+
document.body.appendChild(textarea);
|
|
52
|
+
textarea.select();
|
|
53
|
+
document.execCommand("copy");
|
|
54
|
+
document.body.removeChild(textarea);
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Copies text to the clipboard and briefly updates a DOM element to show
|
|
59
|
+
* visual confirmation (a checkmark for 1 second).
|
|
60
|
+
*
|
|
61
|
+
* @param {string} text - Text to copy.
|
|
62
|
+
* @param {HTMLElement} element - Element whose content is temporarily replaced
|
|
63
|
+
* with a `"✓"` and whose background turns green as feedback.
|
|
64
|
+
* @returns {void}
|
|
65
|
+
*/
|
|
66
|
+
copyWithFeedback: (text, element) => {
|
|
67
|
+
clipboard.copy(text);
|
|
68
|
+
|
|
69
|
+
if (element) {
|
|
70
|
+
const originalText = element.textContent;
|
|
71
|
+
const originalBg = element.style.backgroundColor;
|
|
72
|
+
|
|
73
|
+
element.textContent = "✓";
|
|
74
|
+
element.style.backgroundColor = "#27ae60";
|
|
75
|
+
|
|
76
|
+
setTimeout(() => {
|
|
77
|
+
element.textContent = originalText;
|
|
78
|
+
element.style.backgroundColor = originalBg;
|
|
79
|
+
}, 1000);
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reads the current text content of the clipboard.
|
|
85
|
+
*
|
|
86
|
+
* Unlike `copy`, this method IS async and must be awaited. It returns an
|
|
87
|
+
* empty string on failure (e.g. when permission is denied).
|
|
88
|
+
*
|
|
89
|
+
* @returns {Promise<string>} The clipboard text, or `""` on error.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* const text = await clipboard.read();
|
|
93
|
+
*/
|
|
94
|
+
read: async () => {
|
|
95
|
+
try {
|
|
96
|
+
return await navigator.clipboard.readText();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error("Failed to read:", error);
|
|
99
|
+
return "";
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default clipboard;
|