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,329 @@
|
|
|
1
|
+
// tools/color.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Fluent color manipulation utility.
|
|
5
|
+
*
|
|
6
|
+
* Parses any common CSS color format (hex, rgb/rgba, hsl/hsla, or a small set of
|
|
7
|
+
* named colors), stores the result as normalized RGBA components, and exposes a
|
|
8
|
+
* chainable API for common transformations.
|
|
9
|
+
*
|
|
10
|
+
* Usage pattern: `color(value)` returns a `Color` instance. Chain methods as needed,
|
|
11
|
+
* then call `.hex()`, `.rgb()`, or `.rgba()` to get a CSS string.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* color('#3498db').lighten(20).rgba()
|
|
15
|
+
* // "rgba(91, 174, 226, 1)"
|
|
16
|
+
*
|
|
17
|
+
* color('hsl(200, 60%, 50%)').darken(10).hex()
|
|
18
|
+
* // "#2980b9" (approximately)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @class Color
|
|
23
|
+
* @description Internal color model. Prefer the `color()` factory export over
|
|
24
|
+
* instantiating this class directly.
|
|
25
|
+
*/
|
|
26
|
+
class Color {
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} color - Any CSS color string: hex (`#rgb`, `#rrggbb`),
|
|
29
|
+
* `rgb(r,g,b)`, `rgba(r,g,b,a)`, `hsl(h,s%,l%)`, `hsla(h,s%,l%,a)`,
|
|
30
|
+
* or a named color (see `parseNamed`).
|
|
31
|
+
*/
|
|
32
|
+
constructor(color) {
|
|
33
|
+
this.r = 0;
|
|
34
|
+
this.g = 0;
|
|
35
|
+
this.b = 0;
|
|
36
|
+
this.a = 1;
|
|
37
|
+
this.parse(color);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Detect the format of `color` and dispatch to the appropriate parser.
|
|
42
|
+
*
|
|
43
|
+
* @param {string} color - CSS color string
|
|
44
|
+
* @returns {Color} `this` for chaining
|
|
45
|
+
*/
|
|
46
|
+
parse(color) {
|
|
47
|
+
if (color.startsWith("#")) {
|
|
48
|
+
this.parseHex(color);
|
|
49
|
+
} else if (color.startsWith("rgb")) {
|
|
50
|
+
this.parseRgb(color);
|
|
51
|
+
} else if (color.startsWith("hsl")) {
|
|
52
|
+
this.parseHsl(color);
|
|
53
|
+
} else {
|
|
54
|
+
this.parseNamed(color);
|
|
55
|
+
}
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Parse a hex color string into RGB components.
|
|
61
|
+
*
|
|
62
|
+
* Handles both shorthand (`#rgb`) and full (`#rrggbb`) formats.
|
|
63
|
+
* Alpha channel is not parsed from hex (use rgba/hsla for that).
|
|
64
|
+
*
|
|
65
|
+
* @param {string} hex - Hex color string including the leading `#`
|
|
66
|
+
*/
|
|
67
|
+
parseHex(hex) {
|
|
68
|
+
hex = hex.slice(1);
|
|
69
|
+
if (hex.length === 3) {
|
|
70
|
+
// Expand shorthand: #abc → #aabbcc
|
|
71
|
+
this.r = parseInt(hex[0] + hex[0], 16);
|
|
72
|
+
this.g = parseInt(hex[1] + hex[1], 16);
|
|
73
|
+
this.b = parseInt(hex[2] + hex[2], 16);
|
|
74
|
+
} else {
|
|
75
|
+
this.r = parseInt(hex.slice(0, 2), 16);
|
|
76
|
+
this.g = parseInt(hex.slice(2, 4), 16);
|
|
77
|
+
this.b = parseInt(hex.slice(4, 6), 16);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Parse an `rgb()` or `rgba()` CSS color string.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} rgb - e.g. `"rgb(255, 128, 0)"` or `"rgba(255, 128, 0, 0.5)"`
|
|
85
|
+
*/
|
|
86
|
+
parseRgb(rgb) {
|
|
87
|
+
const match = rgb.match(
|
|
88
|
+
/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/,
|
|
89
|
+
);
|
|
90
|
+
if (match) {
|
|
91
|
+
this.r = parseInt(match[1]);
|
|
92
|
+
this.g = parseInt(match[2]);
|
|
93
|
+
this.b = parseInt(match[3]);
|
|
94
|
+
this.a = match[4] ? parseFloat(match[4]) : 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Parse an `hsl()` or `hsla()` CSS color string, converting to RGB.
|
|
100
|
+
*
|
|
101
|
+
* Uses the standard hue-to-RGB algorithm. Hue is normalized to [0, 1],
|
|
102
|
+
* saturation and lightness to [0, 1].
|
|
103
|
+
*
|
|
104
|
+
* @param {string} hsl - e.g. `"hsl(200, 60%, 40%)"` or `"hsla(200, 60%, 40%, 0.8)"`
|
|
105
|
+
*/
|
|
106
|
+
parseHsl(hsl) {
|
|
107
|
+
const match = hsl.match(
|
|
108
|
+
/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*([\d.]+))?\)/,
|
|
109
|
+
);
|
|
110
|
+
if (match) {
|
|
111
|
+
const h = parseInt(match[1]) / 360;
|
|
112
|
+
const s = parseInt(match[2]) / 100;
|
|
113
|
+
const l = parseInt(match[3]) / 100;
|
|
114
|
+
this.a = match[4] ? parseFloat(match[4]) : 1;
|
|
115
|
+
|
|
116
|
+
let r, g, b;
|
|
117
|
+
if (s === 0) {
|
|
118
|
+
// Achromatic: all channels are equal to lightness
|
|
119
|
+
r = g = b = l;
|
|
120
|
+
} else {
|
|
121
|
+
const hue2rgb = (p, q, t) => {
|
|
122
|
+
if (t < 0) t += 1;
|
|
123
|
+
if (t > 1) t -= 1;
|
|
124
|
+
if (t < 1 / 6) return p + (q - p) * 6 * t;
|
|
125
|
+
if (t < 1 / 2) return q;
|
|
126
|
+
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
|
|
127
|
+
return p;
|
|
128
|
+
};
|
|
129
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
130
|
+
const p = 2 * l - q;
|
|
131
|
+
r = hue2rgb(p, q, h + 1 / 3);
|
|
132
|
+
g = hue2rgb(p, q, h);
|
|
133
|
+
b = hue2rgb(p, q, h - 1 / 3);
|
|
134
|
+
}
|
|
135
|
+
this.r = Math.round(r * 255);
|
|
136
|
+
this.g = Math.round(g * 255);
|
|
137
|
+
this.b = Math.round(b * 255);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse a named CSS color by mapping it to its hex equivalent.
|
|
143
|
+
*
|
|
144
|
+
* Only a small subset of common names is supported. Unknown names are silently
|
|
145
|
+
* ignored (the instance keeps its previous RGB values).
|
|
146
|
+
*
|
|
147
|
+
* @param {string} name - Lowercase color name (e.g. `"red"`, `"white"`, `"gray"`)
|
|
148
|
+
*/
|
|
149
|
+
parseNamed(name) {
|
|
150
|
+
const colors = {
|
|
151
|
+
black: "#000000",
|
|
152
|
+
white: "#ffffff",
|
|
153
|
+
red: "#ff0000",
|
|
154
|
+
green: "#00ff00",
|
|
155
|
+
blue: "#0000ff",
|
|
156
|
+
yellow: "#ffff00",
|
|
157
|
+
cyan: "#00ffff",
|
|
158
|
+
magenta: "#ff00ff",
|
|
159
|
+
gray: "#808080",
|
|
160
|
+
darkgray: "#a9a9a9",
|
|
161
|
+
lightgray: "#d3d3d3",
|
|
162
|
+
};
|
|
163
|
+
if (colors[name]) {
|
|
164
|
+
this.parseHex(colors[name]);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Serialize the color as a hex string.
|
|
170
|
+
*
|
|
171
|
+
* Alpha is not included — use `.rgba()` when you need transparency.
|
|
172
|
+
*
|
|
173
|
+
* @returns {string} Lowercase hex color (e.g. `"#3498db"`)
|
|
174
|
+
*/
|
|
175
|
+
hex() {
|
|
176
|
+
const toHex = (n) => {
|
|
177
|
+
const hex = n.toString(16);
|
|
178
|
+
return hex.length === 1 ? "0" + hex : hex;
|
|
179
|
+
};
|
|
180
|
+
return `#${toHex(this.r)}${toHex(this.g)}${toHex(this.b)}`;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Serialize the color as an `rgb()` CSS string.
|
|
185
|
+
*
|
|
186
|
+
* Alpha is not included. Use `.rgba()` for translucent colors.
|
|
187
|
+
*
|
|
188
|
+
* @returns {string} e.g. `"rgb(52, 152, 219)"`
|
|
189
|
+
*/
|
|
190
|
+
rgb() {
|
|
191
|
+
return `rgb(${this.r}, ${this.g}, ${this.b})`;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Serialize the color as an `rgba()` CSS string including the alpha channel.
|
|
196
|
+
*
|
|
197
|
+
* @returns {string} e.g. `"rgba(52, 152, 219, 1)"`
|
|
198
|
+
*/
|
|
199
|
+
rgba() {
|
|
200
|
+
return `rgba(${this.r}, ${this.g}, ${this.b}, ${this.a})`;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Lighten the color by blending it toward white.
|
|
205
|
+
*
|
|
206
|
+
* Each channel is moved toward 255 by `percent`% of the remaining distance.
|
|
207
|
+
* Mutates this instance and returns `this` for chaining.
|
|
208
|
+
*
|
|
209
|
+
* @param {number} percent - Lightening amount as a percentage (0–100)
|
|
210
|
+
* @returns {Color} `this` for chaining
|
|
211
|
+
* @example color('#336699').lighten(30).hex() // lighter blue
|
|
212
|
+
*/
|
|
213
|
+
lighten(percent) {
|
|
214
|
+
const amount = percent / 100;
|
|
215
|
+
this.r = Math.min(255, Math.round(this.r + (255 - this.r) * amount));
|
|
216
|
+
this.g = Math.min(255, Math.round(this.g + (255 - this.g) * amount));
|
|
217
|
+
this.b = Math.min(255, Math.round(this.b + (255 - this.b) * amount));
|
|
218
|
+
return this;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Darken the color by scaling each channel toward zero.
|
|
223
|
+
*
|
|
224
|
+
* Each channel is multiplied by `(1 - percent/100)`.
|
|
225
|
+
* Mutates this instance and returns `this` for chaining.
|
|
226
|
+
*
|
|
227
|
+
* @param {number} percent - Darkening amount as a percentage (0–100)
|
|
228
|
+
* @returns {Color} `this` for chaining
|
|
229
|
+
* @example color('#3498db').darken(20).hex() // darker blue
|
|
230
|
+
*/
|
|
231
|
+
darken(percent) {
|
|
232
|
+
const amount = percent / 100;
|
|
233
|
+
this.r = Math.max(0, Math.round(this.r * (1 - amount)));
|
|
234
|
+
this.g = Math.max(0, Math.round(this.g * (1 - amount)));
|
|
235
|
+
this.b = Math.max(0, Math.round(this.b * (1 - amount)));
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Set the alpha (opacity) channel.
|
|
241
|
+
*
|
|
242
|
+
* Clamps `value` to the [0, 1] range.
|
|
243
|
+
* Mutates this instance and returns `this` for chaining.
|
|
244
|
+
*
|
|
245
|
+
* @param {number} value - Alpha value between 0 (fully transparent) and 1 (fully opaque)
|
|
246
|
+
* @returns {Color} `this` for chaining
|
|
247
|
+
* @example color('#000').alpha(0.5).rgba() // "rgba(0, 0, 0, 0.5)"
|
|
248
|
+
*/
|
|
249
|
+
alpha(value) {
|
|
250
|
+
this.a = Math.min(1, Math.max(0, value));
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Determine whether the color is perceptually dark.
|
|
256
|
+
*
|
|
257
|
+
* Uses the ITU-R BT.601 luma formula (weighted RGB) which is a standard
|
|
258
|
+
* way to approximate human-perceived brightness. A brightness below 128
|
|
259
|
+
* is considered dark.
|
|
260
|
+
*
|
|
261
|
+
* @returns {boolean} `true` if the color is perceptually dark
|
|
262
|
+
*/
|
|
263
|
+
isDark() {
|
|
264
|
+
const brightness = (this.r * 299 + this.g * 587 + this.b * 114) / 1000;
|
|
265
|
+
return brightness < 128;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Determine whether the color is perceptually light.
|
|
270
|
+
*
|
|
271
|
+
* The inverse of `isDark()`.
|
|
272
|
+
*
|
|
273
|
+
* @returns {boolean} `true` if the color is perceptually light
|
|
274
|
+
*/
|
|
275
|
+
isLight() {
|
|
276
|
+
return !this.isDark();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Get a high-contrast text color suitable for overlay on this background.
|
|
281
|
+
*
|
|
282
|
+
* Returns black on light backgrounds and white on dark backgrounds — useful
|
|
283
|
+
* for automatically picking readable label colors.
|
|
284
|
+
*
|
|
285
|
+
* @returns {"#ffffff"|"#000000"} High-contrast color string
|
|
286
|
+
* @example
|
|
287
|
+
* const bg = color('#3498db');
|
|
288
|
+
* element.style.color = bg.contrast(); // "#ffffff" (white on this mid-blue)
|
|
289
|
+
*/
|
|
290
|
+
contrast() {
|
|
291
|
+
return this.isDark() ? "#ffffff" : "#000000";
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Invert the color by complementing each RGB channel.
|
|
296
|
+
*
|
|
297
|
+
* Each channel is replaced with `255 - channel`. The alpha value is unchanged.
|
|
298
|
+
* Mutates this instance and returns `this` for chaining.
|
|
299
|
+
*
|
|
300
|
+
* @returns {Color} `this` for chaining
|
|
301
|
+
* @example color('#ff0000').complement().hex() // "#00ffff" (cyan)
|
|
302
|
+
*/
|
|
303
|
+
complement() {
|
|
304
|
+
this.r = 255 - this.r;
|
|
305
|
+
this.g = 255 - this.g;
|
|
306
|
+
this.b = 255 - this.b;
|
|
307
|
+
return this;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Create a new `Color` instance from any CSS color string.
|
|
313
|
+
*
|
|
314
|
+
* This is the primary entry point for the color API. The returned object
|
|
315
|
+
* exposes a fluent, chainable interface for parsing, transforming, and
|
|
316
|
+
* serializing colors.
|
|
317
|
+
*
|
|
318
|
+
* @param {string} value - Any CSS color: hex, rgb/rgba, hsl/hsla, or a named color
|
|
319
|
+
* @returns {Color} Chainable color instance
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* color('#e74c3c').darken(10).alpha(0.9).rgba()
|
|
323
|
+
* // "rgba(195, 58, 44, 0.9)"
|
|
324
|
+
*
|
|
325
|
+
* color('hsl(200, 60%, 50%)').complement().hex()
|
|
326
|
+
*/
|
|
327
|
+
export const color = (value) => new Color(value);
|
|
328
|
+
|
|
329
|
+
export default color;
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
// src/components/flet-box/tools/createList.js
|
|
2
|
+
|
|
3
|
+
import { random } from "./random.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a list of items with generated data
|
|
7
|
+
* @module tools/createList
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a list of items from a schema
|
|
12
|
+
* @param {Object} schema - Data schema { field: value or function(item, index) }
|
|
13
|
+
* @param {number} count - Number of items to generate (default: 10)
|
|
14
|
+
* @returns {Array} Generated list
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Basic usage
|
|
18
|
+
* const users = createList({
|
|
19
|
+
* id: () => random.id(),
|
|
20
|
+
* name: random.fullName(),
|
|
21
|
+
* age: () => random.number(18, 65),
|
|
22
|
+
* active: random.boolean()
|
|
23
|
+
* }, 100);
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* // With field dependencies
|
|
27
|
+
* const users = createList({
|
|
28
|
+
* name: random.fullName(),
|
|
29
|
+
* email: (item) => random.email(item.name),
|
|
30
|
+
* age: () => random.number(18, 65)
|
|
31
|
+
* }, 50);
|
|
32
|
+
*/
|
|
33
|
+
export const createList = (schema, count = 10) => {
|
|
34
|
+
const items = [];
|
|
35
|
+
|
|
36
|
+
for (let i = 0; i < count; i++) {
|
|
37
|
+
const item = {};
|
|
38
|
+
|
|
39
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
40
|
+
if (typeof value === "function") {
|
|
41
|
+
// Pass the partially built item and index to the function
|
|
42
|
+
item[key] = value(item, i);
|
|
43
|
+
} else {
|
|
44
|
+
item[key] = value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Auto-add id if not present in schema
|
|
49
|
+
if (!item.hasOwnProperty("id") && !schema.id) {
|
|
50
|
+
item.id = i;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
items.push(item);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return items;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a list from existing data with transformation
|
|
61
|
+
* @param {Array} data - Source data
|
|
62
|
+
* @param {Object} mapper - Mapping functions { targetField: (sourceItem, index) => value }
|
|
63
|
+
* @returns {Array} Transformed list
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* const apiUsers = await fetchUsers();
|
|
67
|
+
* const users = createList.fromData(apiUsers, {
|
|
68
|
+
* id: (user) => user.userId,
|
|
69
|
+
* name: (user) => `${user.firstName} ${user.lastName}`,
|
|
70
|
+
* email: (user) => user.emailAddress
|
|
71
|
+
* });
|
|
72
|
+
*/
|
|
73
|
+
createList.fromData = (data, mapper) => {
|
|
74
|
+
return data.map((item, index) => {
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const [key, transform] of Object.entries(mapper)) {
|
|
77
|
+
result[key] =
|
|
78
|
+
typeof transform === "function" ? transform(item, index) : transform;
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a sequential list of numbers
|
|
86
|
+
* @param {number} start - Start value (inclusive)
|
|
87
|
+
* @param {number} end - End value (inclusive)
|
|
88
|
+
* @param {Function} mapper - Optional mapping function (receives value, index)
|
|
89
|
+
* @returns {Array} Sequential list
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* createList.range(1, 10) // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
|
93
|
+
* createList.range(0, 5, (i) => `Item ${i}`) // ['Item 0', 'Item 1', ...]
|
|
94
|
+
*/
|
|
95
|
+
createList.range = (start, end, mapper = null) => {
|
|
96
|
+
const items = [];
|
|
97
|
+
for (let i = start; i <= end; i++) {
|
|
98
|
+
items.push(mapper ? mapper(i, i - start) : i);
|
|
99
|
+
}
|
|
100
|
+
return items;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create a list with repeated value
|
|
105
|
+
* @param {any} value - Value to repeat
|
|
106
|
+
* @param {number} count - Number of repetitions
|
|
107
|
+
* @returns {Array} Repeated list
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* createList.repeat({ active: true }, 10) // 10 objects with { active: true }
|
|
111
|
+
*/
|
|
112
|
+
createList.repeat = (value, count) => {
|
|
113
|
+
return Array(count).fill(value);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Paginate a list
|
|
118
|
+
* @param {Array} list - Source list
|
|
119
|
+
* @param {number} page - Page number (1-indexed, default: 1)
|
|
120
|
+
* @param {number} pageSize - Items per page (default: 20)
|
|
121
|
+
* @returns {Object} Paginated result with metadata
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* const { data, totalPages, hasNext } = createList.paginate(users, 2, 50);
|
|
125
|
+
*/
|
|
126
|
+
createList.paginate = (list, page = 1, pageSize = 20) => {
|
|
127
|
+
const start = (page - 1) * pageSize;
|
|
128
|
+
const end = start + pageSize;
|
|
129
|
+
return {
|
|
130
|
+
data: list.slice(start, end),
|
|
131
|
+
page,
|
|
132
|
+
pageSize,
|
|
133
|
+
total: list.length,
|
|
134
|
+
totalPages: Math.ceil(list.length / pageSize),
|
|
135
|
+
hasNext: end < list.length,
|
|
136
|
+
hasPrev: page > 1,
|
|
137
|
+
startIndex: start,
|
|
138
|
+
endIndex: Math.min(end, list.length),
|
|
139
|
+
};
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Search/filter a list
|
|
144
|
+
* @param {Array} list - Source list
|
|
145
|
+
* @param {string|Function} query - Search string or filter function
|
|
146
|
+
* @param {string[]} fields - Fields to search (if query is string)
|
|
147
|
+
* @returns {Array} Filtered list
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* createList.search(users, 'Ana', ['name', 'email'])
|
|
151
|
+
* createList.search(users, (user) => user.age > 30)
|
|
152
|
+
*/
|
|
153
|
+
createList.search = (list, query, fields = null) => {
|
|
154
|
+
if (typeof query === "function") {
|
|
155
|
+
return list.filter(query);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const searchTerm = query.toLowerCase();
|
|
159
|
+
return list.filter((item) => {
|
|
160
|
+
const searchableFields = fields || Object.keys(item);
|
|
161
|
+
return searchableFields.some((field) => {
|
|
162
|
+
const value = item[field];
|
|
163
|
+
return value && String(value).toLowerCase().includes(searchTerm);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Sort a list
|
|
170
|
+
* @param {Array} list - Source list
|
|
171
|
+
* @param {string} field - Field to sort by
|
|
172
|
+
* @param {string} order - 'asc' or 'desc' (default: 'asc')
|
|
173
|
+
* @returns {Array} Sorted list
|
|
174
|
+
*/
|
|
175
|
+
createList.sort = (list, field, order = "asc") => {
|
|
176
|
+
const sorted = [...list];
|
|
177
|
+
sorted.sort((a, b) => {
|
|
178
|
+
const aVal = a[field];
|
|
179
|
+
const bVal = b[field];
|
|
180
|
+
|
|
181
|
+
if (typeof aVal === "number" && typeof bVal === "number") {
|
|
182
|
+
return order === "asc" ? aVal - bVal : bVal - aVal;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const aStr = String(aVal).toLowerCase();
|
|
186
|
+
const bStr = String(bVal).toLowerCase();
|
|
187
|
+
|
|
188
|
+
if (order === "asc") {
|
|
189
|
+
return aStr.localeCompare(bStr);
|
|
190
|
+
} else {
|
|
191
|
+
return bStr.localeCompare(aStr);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
return sorted;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Group a list by a field
|
|
199
|
+
* @param {Array} list - Source list
|
|
200
|
+
* @param {string} field - Field to group by
|
|
201
|
+
* @returns {Object} Grouped object
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* const byStatus = createList.groupBy(users, 'status')
|
|
205
|
+
*/
|
|
206
|
+
createList.groupBy = (list, field) => {
|
|
207
|
+
return list.reduce((groups, item) => {
|
|
208
|
+
const key = item[field];
|
|
209
|
+
if (!groups[key]) groups[key] = [];
|
|
210
|
+
groups[key].push(item);
|
|
211
|
+
return groups;
|
|
212
|
+
}, {});
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
export default createList;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// src/components/flet-box/tools/delay.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Delay execution for a specified number of milliseconds.
|
|
5
|
+
*
|
|
6
|
+
* @param {number} ms - Number of milliseconds to wait.
|
|
7
|
+
* @returns {Promise<void>} A promise that resolves after the delay.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* await delay(1000);
|
|
11
|
+
* console.log('After 1 second');
|
|
12
|
+
*/
|
|
13
|
+
export const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ensures a promise takes at least `minMs` milliseconds to resolve.
|
|
17
|
+
* Useful for preventing UI flicker when async operations complete too quickly.
|
|
18
|
+
*
|
|
19
|
+
* @param {Promise<*>} promise - The promise to execute.
|
|
20
|
+
* @param {number} [minMs=500] - Minimum display time in milliseconds.
|
|
21
|
+
* @returns {Promise<*>} Resolves with the promise result after at least `minMs` ms.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* const data = await withMinDelay(fetchData(), 800);
|
|
25
|
+
*/
|
|
26
|
+
export const withMinDelay = async (promise, minMs = 500) => {
|
|
27
|
+
const [result] = await Promise.all([promise, delay(minMs)]);
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Retries an async operation up to `retries` times with a delay between attempts.
|
|
33
|
+
* Throws the last encountered error if all retries are exhausted.
|
|
34
|
+
*
|
|
35
|
+
* @param {Function} fn - Async function to attempt.
|
|
36
|
+
* @param {number} [retries=3] - Maximum number of retry attempts.
|
|
37
|
+
* @param {number} [delayMs=1000] - Milliseconds to wait between retries.
|
|
38
|
+
* @returns {Promise<*>} Resolves with the function's result on success.
|
|
39
|
+
* @throws {Error} Re-throws the last error when all retries are exhausted.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* const data = await retry(() => fetchData(), 3, 1000);
|
|
43
|
+
*/
|
|
44
|
+
export const retry = async (fn, retries = 3, delayMs = 1000) => {
|
|
45
|
+
try {
|
|
46
|
+
return await fn();
|
|
47
|
+
} catch (error) {
|
|
48
|
+
if (retries === 0) throw error;
|
|
49
|
+
await delay(delayMs);
|
|
50
|
+
return retry(fn, retries - 1, delayMs);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Default export for importing everything at once
|
|
55
|
+
export default { delay, withMinDelay, retry };
|