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,38 @@
|
|
|
1
|
+
// src/tools/index.js
|
|
2
|
+
export { animation } from "./animation.js";
|
|
3
|
+
export { shuffle, reverse, sort, unique, chunk } from "./array.js";
|
|
4
|
+
export { border } from "./border.js";
|
|
5
|
+
export { clipboard } from "./clipboard.js";
|
|
6
|
+
export { color } from "./color.js";
|
|
7
|
+
export { createList } from "./createList.js";
|
|
8
|
+
export { delay, withMinDelay, retry } from "./delay.js";
|
|
9
|
+
export { device } from "./device.js";
|
|
10
|
+
export { dimensions, width, height } from "./dimensions.js";
|
|
11
|
+
export { filter } from "./filter.js";
|
|
12
|
+
export { flex } from "./flex.js";
|
|
13
|
+
export { default as gradient } from "./gradient.js";
|
|
14
|
+
export { grid } from "./grid.js";
|
|
15
|
+
export { default as margin } from "./margin.js";
|
|
16
|
+
export { mapList, repeat, range } from "./mapList.js";
|
|
17
|
+
export { memo, memoWithKey, clearMemo } from "./memo.js";
|
|
18
|
+
export { os } from "./os.js";
|
|
19
|
+
export { default as padding } from "./padding.js";
|
|
20
|
+
export { print } from "./print.js";
|
|
21
|
+
export { random } from "./random.js";
|
|
22
|
+
export { default as rgba } from "./rgba.js";
|
|
23
|
+
export { default as shadow } from "./shadow.js";
|
|
24
|
+
export {
|
|
25
|
+
capitalize,
|
|
26
|
+
capitalizeWords,
|
|
27
|
+
lowerCase,
|
|
28
|
+
upperCase,
|
|
29
|
+
reverseString,
|
|
30
|
+
truncate,
|
|
31
|
+
} from "./string.js";
|
|
32
|
+
export { formatDate, relativeTime, sleep, now } from "./time.js";
|
|
33
|
+
export { transform } from "./transform.js";
|
|
34
|
+
export { transition } from "./transition.js";
|
|
35
|
+
export { useState, useWatchState } from "./useState.js";
|
|
36
|
+
export { uuid, shortId, numericId, timestampId } from "./uuid.js";
|
|
37
|
+
export { dict, emptyDict, fromJSON, fromEntries, Dict } from "./dict.js";
|
|
38
|
+
export { ref } from "./ref.js";
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// tools/mapList.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Maps an array, a numeric length, or a plain object to an array of widgets or
|
|
5
|
+
* any other value produced by the mapping function.
|
|
6
|
+
*
|
|
7
|
+
* - **Array**: iterates with `(item, index)`.
|
|
8
|
+
* - **Number**: treats the value as an array length and iterates with `(null, index)`.
|
|
9
|
+
* - **Object**: iterates over `Object.entries` with `(value, key, index)`.
|
|
10
|
+
*
|
|
11
|
+
* @param {Array|number|Object} list - Source collection.
|
|
12
|
+
* @param {Function} fn - Mapping function. Signature depends on source type:
|
|
13
|
+
* - Array: `(item, index) => widget`
|
|
14
|
+
* - Number: `(null, index) => widget`
|
|
15
|
+
* - Object: `(value, key, index) => widget`
|
|
16
|
+
* @returns {Array} Array of mapped values.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Map array to widgets
|
|
20
|
+
* mapList(users, (user, i) => Text({ text: user.name }))
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* // Generateste N widgets from a number
|
|
24
|
+
* mapList(5, (_, i) => Skeleton({ variant: 'text' }))
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* // Map object values
|
|
28
|
+
* mapList({ a: 1, b: 2, c: 3 }, (value, key) => Text({ text: `${key}: ${value}` }))
|
|
29
|
+
*/
|
|
30
|
+
export const mapList = (list, fn) => {
|
|
31
|
+
// Handle number — create a virtual array of that length
|
|
32
|
+
if (typeof list === "number") {
|
|
33
|
+
const arr = [];
|
|
34
|
+
for (let i = 0; i < list; i++) {
|
|
35
|
+
arr.push(fn(null, i));
|
|
36
|
+
}
|
|
37
|
+
return arr;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle array
|
|
41
|
+
if (Array.isArray(list)) {
|
|
42
|
+
return list.map((item, index) => fn(item, index));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Handle plain object — iterate over its entries
|
|
46
|
+
if (typeof list === "object" && list !== null) {
|
|
47
|
+
return Object.entries(list).map(([key, value], index) =>
|
|
48
|
+
fn(value, key, index),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return [];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Repeats a widget (or calls a factory function) `count` times and returns the
|
|
57
|
+
* resulting array.
|
|
58
|
+
*
|
|
59
|
+
* @param {number} count - Number of repetitions.
|
|
60
|
+
* @param {Function|*} widget - Widget instance to repeat, or a `(index) => widget`
|
|
61
|
+
* factory function called on each iteration.
|
|
62
|
+
* @returns {Array} Array of widgets.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* repeat(3, Button({ text: 'Click' }))
|
|
66
|
+
* repeat(5, (i) => Text({ text: `Item ${i}` }))
|
|
67
|
+
*/
|
|
68
|
+
export const repeat = (count, widget) => {
|
|
69
|
+
const result = [];
|
|
70
|
+
for (let i = 0; i < count; i++) {
|
|
71
|
+
if (typeof widget === "function") {
|
|
72
|
+
result.push(widget(i));
|
|
73
|
+
} else {
|
|
74
|
+
result.push(widget);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Generates an array of numbers in the half-open interval `[start, end)` with
|
|
82
|
+
* a configurable step. Mirrors Python's `range()`.
|
|
83
|
+
*
|
|
84
|
+
* When called with a single argument, the range starts at `0`.
|
|
85
|
+
*
|
|
86
|
+
* @param {number} start - Start value (inclusive), or the exclusive end when `end` is omitted.
|
|
87
|
+
* @param {number} [end] - End value (exclusive). If omitted, `start` becomes the end and `0` is used as start.
|
|
88
|
+
* @param {number} [step=1] - Increment between values.
|
|
89
|
+
* @returns {number[]} Array of numbers.
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* range(5) // [0, 1, 2, 3, 4]
|
|
93
|
+
* range(1, 5) // [1, 2, 3, 4]
|
|
94
|
+
* range(0, 10, 2) // [0, 2, 4, 6, 8]
|
|
95
|
+
*/
|
|
96
|
+
export const range = (start, end, step = 1) => {
|
|
97
|
+
const result = [];
|
|
98
|
+
const actualStart = end === undefined ? 0 : start;
|
|
99
|
+
const actualEnd = end === undefined ? start : end;
|
|
100
|
+
|
|
101
|
+
for (let i = actualStart; i < actualEnd; i += step) {
|
|
102
|
+
result.push(i);
|
|
103
|
+
}
|
|
104
|
+
return result;
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default { mapList, repeat, range };
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// tools/margin.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates a CSS `margin` shorthand string from a number, a plain CSS string,
|
|
5
|
+
* or a configuration object.
|
|
6
|
+
*
|
|
7
|
+
* When all resolved sides are equal the output is a single value (e.g. `"20px"`).
|
|
8
|
+
* When only top/bottom differ from left/right the two-value shorthand is used
|
|
9
|
+
* (e.g. `"10px 20px"`). Otherwise the full four-value form is returned.
|
|
10
|
+
*
|
|
11
|
+
* Object option keys (all optional, resolved in priority order per side):
|
|
12
|
+
* - `all` — applies to every side.
|
|
13
|
+
* - `horizontal` — applies to `left` and `right`.
|
|
14
|
+
* - `vertical` — applies to `top` and `bottom`.
|
|
15
|
+
* - `top`, `right`, `bottom`, `left` — individual side overrides.
|
|
16
|
+
*
|
|
17
|
+
* @param {number|string|Object} value
|
|
18
|
+
* A number (converted to `px`), a raw CSS string, or an options object.
|
|
19
|
+
* @returns {string} CSS margin value.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* margin(20) // "20px"
|
|
23
|
+
* margin({ all: 20 }) // "20px"
|
|
24
|
+
* margin({ horizontal: 20, vertical: 10 }) // "10px 20px"
|
|
25
|
+
* margin({ top: 10, right: 20, bottom: 10, left: 20 }) // "10px 20px"
|
|
26
|
+
* margin({ left: 20, right: 20 }) // "0px 20px"
|
|
27
|
+
* margin({ top: 10, bottom: 10 }) // "10px 0px"
|
|
28
|
+
*/
|
|
29
|
+
export const margin = (value) => {
|
|
30
|
+
// A number or raw CSS string applies uniformly to all sides
|
|
31
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
32
|
+
const px = typeof value === "number" ? `${value}px` : value;
|
|
33
|
+
return px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Object form — resolve each side independently
|
|
37
|
+
if (typeof value === "object") {
|
|
38
|
+
const top =
|
|
39
|
+
value.top !== undefined
|
|
40
|
+
? value.top
|
|
41
|
+
: value.vertical !== undefined
|
|
42
|
+
? value.vertical
|
|
43
|
+
: value.all !== undefined
|
|
44
|
+
? value.all
|
|
45
|
+
: 0;
|
|
46
|
+
const right =
|
|
47
|
+
value.right !== undefined
|
|
48
|
+
? value.right
|
|
49
|
+
: value.horizontal !== undefined
|
|
50
|
+
? value.horizontal
|
|
51
|
+
: value.all !== undefined
|
|
52
|
+
? value.all
|
|
53
|
+
: 0;
|
|
54
|
+
const bottom =
|
|
55
|
+
value.bottom !== undefined
|
|
56
|
+
? value.bottom
|
|
57
|
+
: value.vertical !== undefined
|
|
58
|
+
? value.vertical
|
|
59
|
+
: value.all !== undefined
|
|
60
|
+
? value.all
|
|
61
|
+
: 0;
|
|
62
|
+
const left =
|
|
63
|
+
value.left !== undefined
|
|
64
|
+
? value.left
|
|
65
|
+
: value.horizontal !== undefined
|
|
66
|
+
? value.horizontal
|
|
67
|
+
: value.all !== undefined
|
|
68
|
+
? value.all
|
|
69
|
+
: 0;
|
|
70
|
+
|
|
71
|
+
const topPx = typeof top === "number" ? `${top}px` : top;
|
|
72
|
+
const rightPx = typeof right === "number" ? `${right}px` : right;
|
|
73
|
+
const bottomPx = typeof bottom === "number" ? `${bottom}px` : bottom;
|
|
74
|
+
const leftPx = typeof left === "number" ? `${left}px` : left;
|
|
75
|
+
|
|
76
|
+
// All sides equal — return a single value
|
|
77
|
+
if (topPx === rightPx && rightPx === bottomPx && bottomPx === leftPx) {
|
|
78
|
+
return topPx;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Only horizontal and vertical differ — use two-value shorthand
|
|
82
|
+
if (topPx === bottomPx && rightPx === leftPx) {
|
|
83
|
+
return `${topPx} ${rightPx}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Full four-value form
|
|
87
|
+
return `${topPx} ${rightPx} ${bottomPx} ${leftPx}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return "0px";
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default margin;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// tools/memo.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Memoize a function (cache results)
|
|
5
|
+
* @param {Function} fn - Function to memoize
|
|
6
|
+
* @returns {Function} Memoized function
|
|
7
|
+
* @example
|
|
8
|
+
* const expensiveFn = memo((n) => { ... })
|
|
9
|
+
*/
|
|
10
|
+
export const memo = (fn) => {
|
|
11
|
+
const cache = new Map();
|
|
12
|
+
|
|
13
|
+
const memoized = (...args) => {
|
|
14
|
+
const key = JSON.stringify(args);
|
|
15
|
+
if (cache.has(key)) {
|
|
16
|
+
return cache.get(key);
|
|
17
|
+
}
|
|
18
|
+
const result = fn(...args);
|
|
19
|
+
cache.set(key, result);
|
|
20
|
+
return result;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
memoized.cache = cache;
|
|
24
|
+
return memoized;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Memoize with custom key generator
|
|
29
|
+
* @param {Function} fn - Function to memoize
|
|
30
|
+
* @param {Function} keyFn - Key generator function
|
|
31
|
+
* @returns {Function} Memoized function
|
|
32
|
+
*/
|
|
33
|
+
export const memoWithKey = (fn, keyFn) => {
|
|
34
|
+
const cache = new Map();
|
|
35
|
+
|
|
36
|
+
const memoized = (...args) => {
|
|
37
|
+
const key = keyFn(...args);
|
|
38
|
+
if (cache.has(key)) {
|
|
39
|
+
return cache.get(key);
|
|
40
|
+
}
|
|
41
|
+
const result = fn(...args);
|
|
42
|
+
cache.set(key, result);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
memoized.cache = cache;
|
|
47
|
+
return memoized;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Clear memo cache
|
|
52
|
+
* @param {Function} memoizedFn - Memoized function
|
|
53
|
+
*/
|
|
54
|
+
export const clearMemo = (memoizedFn) => {
|
|
55
|
+
if (memoizedFn.cache) {
|
|
56
|
+
memoizedFn.cache.clear();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default { memo, memoWithKey, clearMemo };
|
package/src/tools/os.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// tools/os.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Operating system detection utilities.
|
|
5
|
+
*
|
|
6
|
+
* Parses `navigator.userAgent` to identify the host OS and its version.
|
|
7
|
+
* All methods are synchronous reads — no side effects, no caching.
|
|
8
|
+
*
|
|
9
|
+
* Note: UA-string detection is a best-effort heuristic. Browsers, WebViews,
|
|
10
|
+
* and some privacy settings can spoof or omit UA data. Use these results for
|
|
11
|
+
* progressive enhancement, not security decisions.
|
|
12
|
+
*
|
|
13
|
+
* @namespace os
|
|
14
|
+
*/
|
|
15
|
+
export const os = {
|
|
16
|
+
/**
|
|
17
|
+
* Get the name of the host operating system.
|
|
18
|
+
*
|
|
19
|
+
* Detection order matters: Android is checked before Linux because Android
|
|
20
|
+
* UAs also contain "Linux". ChromeOS (CrOS) is checked last as a subset
|
|
21
|
+
* of Linux-like UAs.
|
|
22
|
+
*
|
|
23
|
+
* @returns {"Windows"|"macOS"|"Android"|"iOS"|"Linux"|"ChromeOS"|"Unknown"}
|
|
24
|
+
* Normalized OS name, or `"Unknown"` when no known pattern matches
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* os.name() // "macOS" on a Mac, "Android" on an Android phone
|
|
28
|
+
*/
|
|
29
|
+
name: () => {
|
|
30
|
+
const ua = navigator.userAgent;
|
|
31
|
+
|
|
32
|
+
if (/Windows/i.test(ua)) return "Windows";
|
|
33
|
+
if (/Mac OS|MacIntel|MacPPC|Mac68K/i.test(ua)) return "macOS";
|
|
34
|
+
if (/Android/i.test(ua)) return "Android";
|
|
35
|
+
if (/iOS|iPhone|iPad|iPod/i.test(ua)) return "iOS";
|
|
36
|
+
if (/Linux/i.test(ua)) return "Linux";
|
|
37
|
+
if (/CrOS/i.test(ua)) return "ChromeOS";
|
|
38
|
+
|
|
39
|
+
return "Unknown";
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the version string of the host operating system.
|
|
44
|
+
*
|
|
45
|
+
* Extracts the version from the UA string using OS-specific regex patterns.
|
|
46
|
+
* On macOS, underscore separators in the UA are normalized to dots
|
|
47
|
+
* (e.g. `"10_15_7"` → `"10.15.7"`).
|
|
48
|
+
*
|
|
49
|
+
* @returns {string} Version string (e.g. `"10.0"`, `"14_2"` normalized to `"14.2"`)
|
|
50
|
+
* or `"Unknown"` when the pattern does not match
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* os.version() // "14.1" on macOS Sonoma, "13.0" on Windows 11
|
|
54
|
+
*/
|
|
55
|
+
version: () => {
|
|
56
|
+
const ua = navigator.userAgent;
|
|
57
|
+
const osName = os.name();
|
|
58
|
+
|
|
59
|
+
if (osName === "Windows") {
|
|
60
|
+
const match = ua.match(/Windows NT (\d+\.\d+)/);
|
|
61
|
+
if (match) return match[1];
|
|
62
|
+
}
|
|
63
|
+
if (osName === "macOS") {
|
|
64
|
+
const match = ua.match(/Mac OS X (\d+[._]\d+[._]\d+)/);
|
|
65
|
+
if (match) return match[1].replace(/_/g, ".");
|
|
66
|
+
}
|
|
67
|
+
if (osName === "Android") {
|
|
68
|
+
const match = ua.match(/Android (\d+\.\d+)/);
|
|
69
|
+
if (match) return match[1];
|
|
70
|
+
}
|
|
71
|
+
if (osName === "iOS") {
|
|
72
|
+
const match = ua.match(/OS (\d+[._]\d+[._]?\d*)/);
|
|
73
|
+
if (match) return match[1].replace(/_/g, ".");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return "Unknown";
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Detect whether the host OS is a mobile operating system.
|
|
81
|
+
*
|
|
82
|
+
* Returns `true` for Android and iOS. Use this when you need OS-level
|
|
83
|
+
* behavior differences rather than viewport-size differences (prefer
|
|
84
|
+
* `device.isMobile()` for viewport-based branching).
|
|
85
|
+
*
|
|
86
|
+
* @returns {boolean} `true` if the OS is Android or iOS
|
|
87
|
+
*/
|
|
88
|
+
isMobile: () => {
|
|
89
|
+
return os.name() === "Android" || os.name() === "iOS";
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Detect whether the host OS is a desktop operating system.
|
|
94
|
+
*
|
|
95
|
+
* The inverse of `os.isMobile()`.
|
|
96
|
+
*
|
|
97
|
+
* @returns {boolean} `true` if the OS is not Android or iOS
|
|
98
|
+
*/
|
|
99
|
+
isDesktop: () => {
|
|
100
|
+
return !os.isMobile();
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export default os;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// tools/padding.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates a CSS `padding` shorthand string from a number, a plain CSS string,
|
|
5
|
+
* or a configuration object.
|
|
6
|
+
*
|
|
7
|
+
* When all resolved sides are equal the output is a single value (e.g. `"20px"`).
|
|
8
|
+
* When only top/bottom differ from left/right the two-value shorthand is used
|
|
9
|
+
* (e.g. `"10px 20px"`). Otherwise the full four-value form is returned.
|
|
10
|
+
*
|
|
11
|
+
* Object option keys (all optional, resolved in priority order per side):
|
|
12
|
+
* - `all` — applies to every side.
|
|
13
|
+
* - `horizontal` — applies to `left` and `right`.
|
|
14
|
+
* - `vertical` — applies to `top` and `bottom`.
|
|
15
|
+
* - `top`, `right`, `bottom`, `left` — individual side overrides.
|
|
16
|
+
*
|
|
17
|
+
* @param {number|string|Object} value
|
|
18
|
+
* A number (converted to `px`), a raw CSS string, or an options object.
|
|
19
|
+
* @returns {string} CSS padding value.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* padding(20) // "20px"
|
|
23
|
+
* padding({ all: 20 }) // "20px"
|
|
24
|
+
* padding({ horizontal: 20, vertical: 10 }) // "10px 20px"
|
|
25
|
+
* padding({ top: 10, right: 20, bottom: 10, left: 20 }) // "10px 20px"
|
|
26
|
+
* padding({ left: 20, right: 20 }) // "0px 20px"
|
|
27
|
+
* padding({ top: 10, bottom: 10 }) // "10px 0px"
|
|
28
|
+
*/
|
|
29
|
+
export const padding = (value) => {
|
|
30
|
+
// A number or raw CSS string applies uniformly to all sides
|
|
31
|
+
if (typeof value === "number" || typeof value === "string") {
|
|
32
|
+
const px = typeof value === "number" ? `${value}px` : value;
|
|
33
|
+
return px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Object form — resolve each side independently
|
|
37
|
+
if (typeof value === "object") {
|
|
38
|
+
const top =
|
|
39
|
+
value.top !== undefined
|
|
40
|
+
? value.top
|
|
41
|
+
: value.vertical !== undefined
|
|
42
|
+
? value.vertical
|
|
43
|
+
: value.all !== undefined
|
|
44
|
+
? value.all
|
|
45
|
+
: 0;
|
|
46
|
+
const right =
|
|
47
|
+
value.right !== undefined
|
|
48
|
+
? value.right
|
|
49
|
+
: value.horizontal !== undefined
|
|
50
|
+
? value.horizontal
|
|
51
|
+
: value.all !== undefined
|
|
52
|
+
? value.all
|
|
53
|
+
: 0;
|
|
54
|
+
const bottom =
|
|
55
|
+
value.bottom !== undefined
|
|
56
|
+
? value.bottom
|
|
57
|
+
: value.vertical !== undefined
|
|
58
|
+
? value.vertical
|
|
59
|
+
: value.all !== undefined
|
|
60
|
+
? value.all
|
|
61
|
+
: 0;
|
|
62
|
+
const left =
|
|
63
|
+
value.left !== undefined
|
|
64
|
+
? value.left
|
|
65
|
+
: value.horizontal !== undefined
|
|
66
|
+
? value.horizontal
|
|
67
|
+
: value.all !== undefined
|
|
68
|
+
? value.all
|
|
69
|
+
: 0;
|
|
70
|
+
|
|
71
|
+
const topPx = typeof top === "number" ? `${top}px` : top;
|
|
72
|
+
const rightPx = typeof right === "number" ? `${right}px` : right;
|
|
73
|
+
const bottomPx = typeof bottom === "number" ? `${bottom}px` : bottom;
|
|
74
|
+
const leftPx = typeof left === "number" ? `${left}px` : left;
|
|
75
|
+
|
|
76
|
+
// All sides equal — return a single value
|
|
77
|
+
if (topPx === rightPx && rightPx === bottomPx && bottomPx === leftPx) {
|
|
78
|
+
return topPx;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Only horizontal and vertical differ — use two-value shorthand
|
|
82
|
+
if (topPx === bottomPx && rightPx === leftPx) {
|
|
83
|
+
return `${topPx} ${rightPx}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Full four-value form
|
|
87
|
+
return `${topPx} ${rightPx} ${bottomPx} ${leftPx}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return "0px";
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export default padding;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// tools/print.js
|
|
2
|
+
// Simple and clean logging system
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A thin wrapper around `console` that adds icons, optional timestamps, and
|
|
6
|
+
* optional labels to log output. Objects are serialized to pretty-printed JSON.
|
|
7
|
+
*
|
|
8
|
+
* @param {*} message - Value to log. Objects are serialized with `JSON.stringify`.
|
|
9
|
+
* @param {"log"|"warn"|"error"|"info"|"success"|"debug"} [type="log"] - Log level.
|
|
10
|
+
* Can also be passed as an options object for backwards compatibility.
|
|
11
|
+
* @param {Object} [options={}] - Additional formatting options.
|
|
12
|
+
* @param {boolean} [options.timestamp=false] - Prepend the current time.
|
|
13
|
+
* @param {string} [options.label=""] - Bracket-wrapped label inserted before the message.
|
|
14
|
+
* @param {boolean} [options.prefix=true] - Whether to prepend the level icon.
|
|
15
|
+
* @returns {void}
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* print('Hello world')
|
|
19
|
+
* print('User logged in', 'success')
|
|
20
|
+
* print('Something went wrong', 'error')
|
|
21
|
+
* print('Loading...', 'info', { timestamp: true })
|
|
22
|
+
*/
|
|
23
|
+
export const print = (message, type = "log", options = {}) => {
|
|
24
|
+
// Allow the second argument to be an options object for backwards compatibility
|
|
25
|
+
if (typeof type === "object") {
|
|
26
|
+
options = type;
|
|
27
|
+
type = "log";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { timestamp = false, label = "", prefix = true } = options;
|
|
31
|
+
|
|
32
|
+
const styles = {
|
|
33
|
+
log: { color: "#94a3b8", icon: "📝", method: "log" },
|
|
34
|
+
warn: { color: "#fbbf24", icon: "⚠️", method: "warn" },
|
|
35
|
+
error: { color: "#f87171", icon: "❌", method: "error" },
|
|
36
|
+
info: { color: "#60a5fa", icon: "ℹ️", method: "info" },
|
|
37
|
+
success: { color: "#34d399", icon: "✅", method: "log" },
|
|
38
|
+
debug: { color: "#c084fc", icon: "🐛", method: "debug" },
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const config = styles[type] || styles.log;
|
|
42
|
+
|
|
43
|
+
// Serialize objects to readable JSON
|
|
44
|
+
let formattedMessage = message;
|
|
45
|
+
if (typeof message === "object" && message !== null) {
|
|
46
|
+
formattedMessage = JSON.stringify(message, null, 2);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const parts = [];
|
|
50
|
+
if (timestamp) parts.push(`[${new Date().toLocaleTimeString()}]`);
|
|
51
|
+
if (prefix) parts.push(config.icon);
|
|
52
|
+
if (label) parts.push(`[${label}]`);
|
|
53
|
+
parts.push(formattedMessage);
|
|
54
|
+
|
|
55
|
+
const finalMessage = parts.join(" ");
|
|
56
|
+
|
|
57
|
+
// Dispatch to the appropriate console method for the log level
|
|
58
|
+
console[config.method](finalMessage);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Shorthand aliases (uncomment to enable individual exports):
|
|
62
|
+
// export const log = (msg) => print(msg, "log");
|
|
63
|
+
// export const warn = (msg) => print(msg, "warn");
|
|
64
|
+
// export const error = (msg) => print(msg, "error");
|
|
65
|
+
// export const info = (msg) => print(msg, "info");
|
|
66
|
+
// export const success = (msg) => print(msg, "success");
|
|
67
|
+
// export const debug = (msg) => print(msg, "debug");
|
|
68
|
+
|
|
69
|
+
export default print;
|