flet-box 1.0.7 → 1.0.9
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/README.md +3 -0
- package/bin/cli.js +13 -0
- package/bin/commands/checkApi.js +13 -1
- package/bin/commands/checkUi.js +1369 -0
- package/bin/commands/create.js +49 -5
- package/bin/commands/createBundle.js +21 -0
- package/bin/commands/runServer.js +11 -2
- package/bin/utils/pwaShell.js +137 -0
- package/bin/utils/templates.js +1398 -443
- package/docs/README.md +4 -0
- package/docs/cli/README.md +63 -2
- package/docs/guides/README.md +2 -0
- package/docs/guides/clean-ui.md +211 -0
- package/docs/guides/minification-and-protection.md +1 -1
- package/docs/guides/mobile-and-platforms.md +31 -0
- package/docs/guides/online-updates.md +48 -0
- package/docs/tools/README.md +1 -1
- package/docs/tools/animation.md +1 -1
- package/docs/tools/markdown.md +3 -3
- package/docs/tools/theme.md +42 -1
- package/docs/widget/Accordion.md +3 -0
- package/docs/widget/AdMob.md +4 -1
- package/docs/widget/AdSense.md +3 -0
- package/docs/widget/AnimatedBox.md +6 -3
- package/docs/widget/AnimatedText.md +3 -0
- package/docs/widget/Audio.md +3 -0
- package/docs/widget/Avatar.md +3 -0
- package/docs/widget/Badge.md +3 -0
- package/docs/widget/Button.md +3 -0
- package/docs/widget/COMMON_PROPS.md +52 -0
- package/docs/widget/Card.md +3 -0
- package/docs/widget/Carousel.md +3 -0
- package/docs/widget/Chart.md +3 -0
- package/docs/widget/Checkbox.md +3 -0
- package/docs/widget/Chip.md +3 -0
- package/docs/widget/CircularBar.md +3 -0
- package/docs/widget/CircularChart.md +3 -0
- package/docs/widget/CodeViewer.md +3 -0
- package/docs/widget/Column.md +3 -0
- package/docs/widget/Container.md +3 -0
- package/docs/widget/DataTable.md +3 -0
- package/docs/widget/Divider.md +3 -0
- package/docs/widget/DraggBox.md +3 -0
- package/docs/widget/Dropdown.md +3 -0
- package/docs/widget/DroppBox.md +3 -0
- package/docs/widget/FloatingActionButton.md +3 -0
- package/docs/widget/GridView.md +3 -0
- package/docs/widget/Icon.md +3 -0
- package/docs/widget/Image.md +3 -0
- package/docs/widget/Input.md +3 -0
- package/docs/widget/Inspector.md +2 -2
- package/docs/widget/InstallButton.md +3 -0
- package/docs/widget/ListTile.md +3 -0
- package/docs/widget/ListView.md +3 -0
- package/docs/widget/Maps.md +3 -0
- package/docs/widget/Markdown.md +3 -0
- package/docs/widget/MatrixRain.md +3 -0
- package/docs/widget/Pagination.md +3 -0
- package/docs/widget/ParallaxBox.md +3 -0
- package/docs/widget/ProgressBar.md +3 -0
- package/docs/widget/QRCode.md +3 -0
- package/docs/widget/README.md +2 -0
- package/docs/widget/Radio.md +4 -1
- package/docs/widget/Rating.md +3 -0
- package/docs/widget/Row.md +3 -0
- package/docs/widget/Skeleton.md +3 -0
- package/docs/widget/Slider.md +3 -0
- package/docs/widget/Stack.md +3 -0
- package/docs/widget/Stepper.md +3 -0
- package/docs/widget/Switch.md +3 -0
- package/docs/widget/Tabs.md +5 -5
- package/docs/widget/Text.md +3 -0
- package/docs/widget/Tooltip.md +3 -0
- package/docs/widget/TreeView.md +3 -0
- package/docs/widget/Video.md +3 -0
- package/package.json +2 -2
- package/src/animations/AnimatedBox.js +4 -1
- package/src/animations/MatrixRain.js +3 -3
- package/src/index.d.ts +64 -8
- package/src/index.js +1 -0
- package/src/navigations/AppBar.js +7 -12
- package/src/navigations/BottomNavigation.js +8 -13
- package/src/navigations/CollapsibleSideBar.js +30 -8
- package/src/navigations/Drawer.js +16 -14
- package/src/navigations/DrawerItem.js +3 -1
- package/src/navigations/Scaffold.js +26 -12
- package/src/navigations/Tabs.js +104 -59
- package/src/tools/color.js +54 -0
- package/src/tools/index.js +9 -1
- package/src/tools/scale.js +41 -0
- package/src/tools/shadow.js +20 -0
- package/src/utils/index.js +1 -0
- package/src/utils/readableInk.js +19 -0
- package/src/utils/syntaxHighlight.js +78 -10
- package/src/utils/themes.js +265 -58
- package/src/utils/units.js +33 -11
- package/src/widget-factory/effects.js +56 -5
- package/src/widget-factory/processProps.js +10 -1
- package/src/widget-factory/remTool.js +15 -29
- package/src/widget-factory/stackPosition.js +18 -3
- package/src/widget-factory/translateProps.js +28 -0
- package/src/widgets/Accordion.js +20 -23
- package/src/widgets/AdMob.js +7 -2
- package/src/widgets/Avatar.js +18 -12
- package/src/widgets/Badge.js +3 -2
- package/src/widgets/BottomSheet.js +14 -13
- package/src/widgets/Button.js +69 -30
- package/src/widgets/Card.js +6 -2
- package/src/widgets/Carousel.js +12 -10
- package/src/widgets/Chart.js +28 -5
- package/src/widgets/Checkbox.js +76 -18
- package/src/widgets/Chip.js +31 -10
- package/src/widgets/CircularBar.js +7 -7
- package/src/widgets/CircularChart.js +26 -8
- package/src/widgets/CodeViewer.js +93 -20
- package/src/widgets/DataTable.js +17 -11
- package/src/widgets/DraggBox.js +2 -1
- package/src/widgets/Dropdown.js +26 -13
- package/src/widgets/DroppBox.js +4 -2
- package/src/widgets/FloatingActionButton.js +13 -7
- package/src/widgets/Icon.js +3 -1
- package/src/widgets/Input.js +30 -23
- package/src/widgets/Inspector.js +54 -1
- package/src/widgets/InstallButton.js +6 -4
- package/src/widgets/ListTile.js +4 -3
- package/src/widgets/ListView.js +30 -12
- package/src/widgets/Markdown.js +37 -26
- package/src/widgets/Modal.js +14 -11
- package/src/widgets/Pagination.js +16 -11
- package/src/widgets/ProgressBar.js +6 -4
- package/src/widgets/Radio.js +73 -15
- package/src/widgets/Rating.js +24 -6
- package/src/widgets/Skeleton.js +31 -31
- package/src/widgets/Slider.js +29 -18
- package/src/widgets/SnackBar.js +35 -39
- package/src/widgets/Stepper.js +13 -9
- package/src/widgets/Switch.js +59 -18
- package/src/widgets/Tooltip.js +12 -10
- package/src/widgets/TreeView.js +27 -15
package/src/widgets/DataTable.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
6
|
import { colors } from "../utils/themes.js";
|
|
7
7
|
|
|
8
|
+
import { toREM } from "../utils/units.js";
|
|
9
|
+
|
|
8
10
|
export const DataTable = (props) => {
|
|
9
11
|
const {
|
|
10
12
|
columns,
|
|
@@ -22,7 +24,7 @@ export const DataTable = (props) => {
|
|
|
22
24
|
|
|
23
25
|
rowBgColor = "transparent",
|
|
24
26
|
rowTextColor = colors.text,
|
|
25
|
-
rowFontSize =
|
|
27
|
+
rowFontSize = 14,
|
|
26
28
|
|
|
27
29
|
stripedRowBgColor = colors.gray50,
|
|
28
30
|
|
|
@@ -31,8 +33,12 @@ export const DataTable = (props) => {
|
|
|
31
33
|
borderColor = colors.border,
|
|
32
34
|
borderWidth = 1,
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
// A padding the caller can hand as `8`, `"8px 12px"` or `"1rem"`: every
|
|
37
|
+
// write below goes through toREM, which scales the first two and leaves the
|
|
38
|
+
// third alone. Written straight to `style`, a bare number is an invalid CSS
|
|
39
|
+
// value the browser drops, and a string is a frozen pixel.
|
|
40
|
+
cellPadding = "8px 12px",
|
|
41
|
+
headerCellPadding = 12,
|
|
36
42
|
|
|
37
43
|
// ========== OTHER PROPS ==========
|
|
38
44
|
...rest
|
|
@@ -61,7 +67,7 @@ export const DataTable = (props) => {
|
|
|
61
67
|
for (const col of columns) {
|
|
62
68
|
const th = document.createElement("th");
|
|
63
69
|
th.textContent = typeof col === "string" ? col : col.label;
|
|
64
|
-
th.style.padding = headerCellPadding;
|
|
70
|
+
th.style.padding = toREM(headerCellPadding);
|
|
65
71
|
th.style.textAlign = "left";
|
|
66
72
|
th.style.borderBottom = `2px solid ${borderColor}`;
|
|
67
73
|
th.style.backgroundColor = headerBgColor;
|
|
@@ -69,7 +75,7 @@ export const DataTable = (props) => {
|
|
|
69
75
|
th.style.fontWeight = headerFontWeight;
|
|
70
76
|
th.style.fontSize =
|
|
71
77
|
typeof headerFontSize === "number"
|
|
72
|
-
?
|
|
78
|
+
? toREM(headerFontSize)
|
|
73
79
|
: headerFontSize;
|
|
74
80
|
|
|
75
81
|
// Custom alignment per column
|
|
@@ -122,11 +128,11 @@ export const DataTable = (props) => {
|
|
|
122
128
|
const value = row[key];
|
|
123
129
|
|
|
124
130
|
td.textContent = value !== undefined ? String(value) : "";
|
|
125
|
-
td.style.padding = cellPadding;
|
|
131
|
+
td.style.padding = toREM(cellPadding);
|
|
126
132
|
td.style.borderBottom = `${borderWidth}px solid ${borderColor}`;
|
|
127
133
|
td.style.color = rowTextColor;
|
|
128
134
|
td.style.fontSize =
|
|
129
|
-
|
|
135
|
+
toREM(rowFontSize);
|
|
130
136
|
|
|
131
137
|
// Custom alignment per column
|
|
132
138
|
if (typeof col !== "string" && col.align) {
|
|
@@ -195,11 +201,11 @@ export const DataTable = (props) => {
|
|
|
195
201
|
const value = row[key];
|
|
196
202
|
|
|
197
203
|
td.textContent = value !== undefined ? String(value) : "";
|
|
198
|
-
td.style.padding = cellPadding;
|
|
204
|
+
td.style.padding = toREM(cellPadding);
|
|
199
205
|
td.style.borderBottom = `${borderWidth}px solid ${borderColor}`;
|
|
200
206
|
td.style.color = rowTextColor;
|
|
201
207
|
td.style.fontSize =
|
|
202
|
-
|
|
208
|
+
toREM(rowFontSize);
|
|
203
209
|
|
|
204
210
|
if (typeof col !== "string" && col.align) {
|
|
205
211
|
td.style.textAlign = col.align;
|
|
@@ -236,7 +242,7 @@ export const DataTable = (props) => {
|
|
|
236
242
|
for (const col of newColumns) {
|
|
237
243
|
const th = document.createElement("th");
|
|
238
244
|
th.textContent = typeof col === "string" ? col : col.label;
|
|
239
|
-
th.style.padding = headerCellPadding;
|
|
245
|
+
th.style.padding = toREM(headerCellPadding);
|
|
240
246
|
th.style.textAlign = "left";
|
|
241
247
|
th.style.borderBottom = `2px solid ${borderColor}`;
|
|
242
248
|
th.style.backgroundColor = headerBgColor;
|
|
@@ -244,7 +250,7 @@ export const DataTable = (props) => {
|
|
|
244
250
|
th.style.fontWeight = headerFontWeight;
|
|
245
251
|
th.style.fontSize =
|
|
246
252
|
typeof headerFontSize === "number"
|
|
247
|
-
?
|
|
253
|
+
? toREM(headerFontSize)
|
|
248
254
|
: headerFontSize;
|
|
249
255
|
|
|
250
256
|
if (typeof col !== "string" && col.align) {
|
package/src/widgets/DraggBox.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
6
|
import { colors } from "../utils/themes.js";
|
|
7
|
+
import { toREM } from "../utils/units.js";
|
|
7
8
|
|
|
8
9
|
export const DraggBox = (props) => {
|
|
9
10
|
const {
|
|
@@ -53,7 +54,7 @@ export const DraggBox = (props) => {
|
|
|
53
54
|
clone.style.zIndex = "99999";
|
|
54
55
|
clone.style.backgroundColor = dragOverlayColor;
|
|
55
56
|
clone.style.border = `2px dashed ${dragBorderColor}`;
|
|
56
|
-
clone.style.borderRadius =
|
|
57
|
+
clone.style.borderRadius = toREM(8);
|
|
57
58
|
document.body.appendChild(clone);
|
|
58
59
|
return clone;
|
|
59
60
|
};
|
package/src/widgets/Dropdown.js
CHANGED
|
@@ -12,6 +12,8 @@ import { Row } from "./Row.js";
|
|
|
12
12
|
import { Column } from "./Column.js";
|
|
13
13
|
import { Text } from "./Text.js";
|
|
14
14
|
import { Icon } from "./Icon.js";
|
|
15
|
+
import { toREM } from "../utils/units.js";
|
|
16
|
+
import { TAP_MINIMUM } from "../tools/index.js";
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Props owned by the Dropdown's closure state. Routed to the widget's `update`
|
|
@@ -97,10 +99,12 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
97
99
|
let resizeListener = null;
|
|
98
100
|
let scrollListener = null;
|
|
99
101
|
|
|
102
|
+
/** [vertical, horizontal] in framework units — see `Input`: a `"8px 14px"`
|
|
103
|
+
* string is raw CSS and stops the field growing with the reader's font. */
|
|
100
104
|
const sizes = {
|
|
101
|
-
small: { padding:
|
|
102
|
-
medium: { padding:
|
|
103
|
-
large: { padding:
|
|
105
|
+
small: { padding: [4, 12], fontSize: 12, minHeight: 32 },
|
|
106
|
+
medium: { padding: [8, 12], fontSize: 14, minHeight: TAP_MINIMUM },
|
|
107
|
+
large: { padding: [12, 16], fontSize: 16, minHeight: 48 },
|
|
104
108
|
};
|
|
105
109
|
let currentSize = sizes[size] || sizes.medium;
|
|
106
110
|
|
|
@@ -197,14 +201,16 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
197
201
|
alignItems: "center",
|
|
198
202
|
justifyContent: "space-between",
|
|
199
203
|
width: "100%",
|
|
200
|
-
|
|
204
|
+
paddingVertical: currentSize.padding[0],
|
|
205
|
+
paddingHorizontal: currentSize.padding[1],
|
|
206
|
+
minHeight: currentSize.minHeight,
|
|
201
207
|
backgroundColor:
|
|
202
208
|
currentVariant === "filled" ? `${currentBgColor}CC` : "transparent",
|
|
203
209
|
border:
|
|
204
210
|
currentVariant === "outlined"
|
|
205
211
|
? `1px solid ${currentError ? colors.danger : colors.border}`
|
|
206
212
|
: "none",
|
|
207
|
-
borderRadius:
|
|
213
|
+
borderRadius: currentBorderRadius,
|
|
208
214
|
cursor: currentDisabled ? "not-allowed" : "pointer",
|
|
209
215
|
opacity: currentDisabled ? 0.5 : 1,
|
|
210
216
|
boxSizing: "border-box",
|
|
@@ -236,11 +242,15 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
236
242
|
],
|
|
237
243
|
});
|
|
238
244
|
|
|
239
|
-
//
|
|
245
|
+
// The root must not paint a surface: only the selector does, and an
|
|
246
|
+
// `outlined` selector is deliberately transparent. Container's default
|
|
247
|
+
// background otherwise showed through behind the label as a full-width
|
|
248
|
+
// block, and painted the outlined field as if it were filled.
|
|
240
249
|
const container = Container({
|
|
241
250
|
widgetName: "Dropdown",
|
|
242
251
|
position: "relative",
|
|
243
252
|
width: width,
|
|
253
|
+
bgColor: "transparent",
|
|
244
254
|
...rest,
|
|
245
255
|
});
|
|
246
256
|
|
|
@@ -248,9 +258,9 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
248
258
|
const labelEl = WidgetFactory({
|
|
249
259
|
tag: "label",
|
|
250
260
|
textContent: currentLabel,
|
|
251
|
-
fontSize:
|
|
261
|
+
fontSize: 12,
|
|
252
262
|
color: currentError ? colors.danger : colors.textSecondary,
|
|
253
|
-
marginBottom:
|
|
263
|
+
marginBottom: 4,
|
|
254
264
|
display: "block",
|
|
255
265
|
});
|
|
256
266
|
container.appendChild(labelEl);
|
|
@@ -318,7 +328,7 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
318
328
|
zIndex: 10000,
|
|
319
329
|
border: `1px solid ${colors.border}`,
|
|
320
330
|
backgroundColor: currentBgColor,
|
|
321
|
-
borderRadius:
|
|
331
|
+
borderRadius: currentBorderRadius, // same borderRadius
|
|
322
332
|
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
323
333
|
maxHeight: "250px",
|
|
324
334
|
overflowY: "auto",
|
|
@@ -348,12 +358,14 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
348
358
|
);
|
|
349
359
|
|
|
350
360
|
const optionItem = Row({
|
|
351
|
-
|
|
361
|
+
paddingVertical: 8,
|
|
362
|
+
paddingHorizontal: 12,
|
|
363
|
+
minHeight: TAP_MINIMUM,
|
|
352
364
|
cursor: "pointer",
|
|
353
365
|
backgroundColor: isSelected ? currentColor + "15" : "transparent",
|
|
354
366
|
alignItems: "center",
|
|
355
367
|
justifyContent: "space-between",
|
|
356
|
-
gap:
|
|
368
|
+
gap: 8,
|
|
357
369
|
children: [
|
|
358
370
|
Row({
|
|
359
371
|
alignItems: "center",
|
|
@@ -496,8 +508,9 @@ export const Dropdown = (inputProps = {}) => {
|
|
|
496
508
|
}
|
|
497
509
|
if (needsDisplayUpdate) {
|
|
498
510
|
updateDisplay();
|
|
499
|
-
selector.style.padding = currentSize.padding
|
|
500
|
-
selector.style.
|
|
511
|
+
selector.style.padding = `${toREM(currentSize.padding[0])} ${toREM(currentSize.padding[1])}`;
|
|
512
|
+
selector.style.minHeight = toREM(currentSize.minHeight);
|
|
513
|
+
selector.style.borderRadius = toREM(currentBorderRadius);
|
|
501
514
|
if (currentVariant === "outlined")
|
|
502
515
|
selector.style.border = `1px solid ${currentError ? colors.danger : colors.border}`;
|
|
503
516
|
else selector.style.border = "none";
|
package/src/widgets/DroppBox.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
6
|
import { colors } from "../utils/themes.js";
|
|
7
7
|
|
|
8
|
+
import { toREM } from "../utils/units.js";
|
|
9
|
+
|
|
8
10
|
export const DroppBox = (props) => {
|
|
9
11
|
const {
|
|
10
12
|
child,
|
|
@@ -58,12 +60,12 @@ export const DroppBox = (props) => {
|
|
|
58
60
|
transition: `all ${transitionDuration} ${transitionTiming}`,
|
|
59
61
|
backgroundColor: bgColor,
|
|
60
62
|
borderRadius:
|
|
61
|
-
|
|
63
|
+
toREM(borderRadius),
|
|
62
64
|
borderWidth: `${borderWidth}px`,
|
|
63
65
|
borderStyle: borderStyle,
|
|
64
66
|
borderColor: borderColor,
|
|
65
67
|
boxShadow: shadow,
|
|
66
|
-
padding:
|
|
68
|
+
padding: toREM(padding),
|
|
67
69
|
...rest,
|
|
68
70
|
});
|
|
69
71
|
|
|
@@ -7,6 +7,8 @@ import { Row } from "./Row.js";
|
|
|
7
7
|
import { Text } from "./Text.js";
|
|
8
8
|
import { Icon } from "./Icon.js";
|
|
9
9
|
import { colors } from "../utils/themes.js";
|
|
10
|
+
import { toREM } from "../utils/units.js";
|
|
11
|
+
import { SHADOW_LEVELS, PILL } from "../tools/index.js";
|
|
10
12
|
|
|
11
13
|
export const FloatingActionButton = (props) => {
|
|
12
14
|
const {
|
|
@@ -14,9 +16,11 @@ export const FloatingActionButton = (props) => {
|
|
|
14
16
|
label,
|
|
15
17
|
onPress,
|
|
16
18
|
backgroundColor = colors.primary,
|
|
17
|
-
foregroundColor =
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
foregroundColor = colors.onPrimary,
|
|
20
|
+
// Depth is the 0-5 scale, like every other widget; 6 was a number the scale
|
|
21
|
+
// does not have, which only worked because this widget built its own string.
|
|
22
|
+
elevation = 4,
|
|
23
|
+
borderRadius = PILL,
|
|
20
24
|
padding = 12,
|
|
21
25
|
mini = false,
|
|
22
26
|
extended = false,
|
|
@@ -24,7 +28,9 @@ export const FloatingActionButton = (props) => {
|
|
|
24
28
|
...rest
|
|
25
29
|
} = props;
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
// 48 and 40 are both scale steps, and both clear the Fitts floor: the mini FAB
|
|
32
|
+
// sits exactly on it, which is as small as a floating target may get.
|
|
33
|
+
const size = mini ? 40 : 48;
|
|
28
34
|
const iconSize = mini ? 20 : 24;
|
|
29
35
|
|
|
30
36
|
const children = [
|
|
@@ -58,14 +64,14 @@ export const FloatingActionButton = (props) => {
|
|
|
58
64
|
gap: 8,
|
|
59
65
|
width: extended ? "auto" : size,
|
|
60
66
|
height: size,
|
|
61
|
-
borderRadius: extended ?
|
|
67
|
+
borderRadius: extended ? borderRadius : "50%",
|
|
62
68
|
backgroundColor: backgroundColor,
|
|
63
69
|
color: foregroundColor,
|
|
64
70
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
65
71
|
opacity: disabled ? 0.6 : 1,
|
|
66
|
-
boxShadow:
|
|
72
|
+
boxShadow: SHADOW_LEVELS[elevation] ?? SHADOW_LEVELS[4],
|
|
67
73
|
transition: "all 0.2s ease",
|
|
68
|
-
padding: extended ?
|
|
74
|
+
padding: extended ? `0 ${toREM(16)}` : 0,
|
|
69
75
|
child: extended
|
|
70
76
|
? Row({ alignItems: "center", gap: 8, children })
|
|
71
77
|
: children[0],
|
package/src/widgets/Icon.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
6
|
import { colors } from "../utils/themes.js";
|
|
7
7
|
|
|
8
|
+
import { toREM } from "../utils/units.js";
|
|
9
|
+
|
|
8
10
|
export const Icon = (props) => {
|
|
9
11
|
const {
|
|
10
12
|
icon,
|
|
@@ -20,7 +22,7 @@ export const Icon = (props) => {
|
|
|
20
22
|
widgetName: "Icon",
|
|
21
23
|
className: "material-icons",
|
|
22
24
|
text: iconName, // ✅ CHANGED: text instead of textContent
|
|
23
|
-
fontSize:
|
|
25
|
+
fontSize: toREM(size),
|
|
24
26
|
color: color,
|
|
25
27
|
display: "inline-flex",
|
|
26
28
|
alignItems: "center",
|
package/src/widgets/Input.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
6
|
import { colors } from "../utils/themes.js";
|
|
7
|
+
import { toREM } from "../utils/units.js";
|
|
8
|
+
import { TAP_MINIMUM } from "../tools/index.js";
|
|
7
9
|
import { Icon } from "./Icon.js";
|
|
8
10
|
import { TextInputValidator } from "../utils/TextInputValidator.js";
|
|
9
11
|
import { composeUpdate } from "../utils/composeUpdate.js";
|
|
@@ -76,7 +78,7 @@ export const Input = (props) => {
|
|
|
76
78
|
size = "medium",
|
|
77
79
|
variant = "outlined",
|
|
78
80
|
fullWidth = false,
|
|
79
|
-
borderRadius =
|
|
81
|
+
borderRadius = 16,
|
|
80
82
|
|
|
81
83
|
iconLeft,
|
|
82
84
|
iconRight,
|
|
@@ -131,11 +133,17 @@ export const Input = (props) => {
|
|
|
131
133
|
const isFocused = () => document.activeElement === inputElement;
|
|
132
134
|
|
|
133
135
|
// ========== SIZES ==========
|
|
134
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Framework units, not CSS strings: this widget builds a real `<input>` and
|
|
138
|
+
* writes `style.*` by hand, so every length here goes through `toREM` at the
|
|
139
|
+
* point of use. Written as `"10px 14px"` the field froze at 16px base and the
|
|
140
|
+
* reader who raises their font got small text inside a control that should have
|
|
141
|
+
* grown with it. `minHeight` is the Fitts floor — a text field is a target.
|
|
142
|
+
*/
|
|
135
143
|
const sizes = {
|
|
136
|
-
small: { padding:
|
|
137
|
-
medium: { padding:
|
|
138
|
-
large: { padding:
|
|
144
|
+
small: { padding: [4, 12], fontSize: 12, iconSize: 16, minHeight: 32 },
|
|
145
|
+
medium: { padding: [8, 12], fontSize: 14, iconSize: 20, minHeight: TAP_MINIMUM },
|
|
146
|
+
large: { padding: [12, 16], fontSize: 16, iconSize: 24, minHeight: 48 },
|
|
139
147
|
};
|
|
140
148
|
const sz = sizes[size] || sizes.medium;
|
|
141
149
|
|
|
@@ -350,7 +358,7 @@ export const Input = (props) => {
|
|
|
350
358
|
fontSize: 12,
|
|
351
359
|
fontWeight: 500,
|
|
352
360
|
color: error ? colors.danger : colors.textSecondary,
|
|
353
|
-
marginBottom:
|
|
361
|
+
marginBottom: 4,
|
|
354
362
|
cursor: "text",
|
|
355
363
|
});
|
|
356
364
|
// Clicking the label focuses the native input — mirrors native <label for> behavior
|
|
@@ -387,8 +395,7 @@ export const Input = (props) => {
|
|
|
387
395
|
|
|
388
396
|
if (variant === "filled") {
|
|
389
397
|
wrapperStyles.backgroundColor = colors.gray100;
|
|
390
|
-
wrapperStyles.borderRadius =
|
|
391
|
-
typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
|
|
398
|
+
wrapperStyles.borderRadius = toREM(borderRadius);
|
|
392
399
|
wrapperStyles.border = "none";
|
|
393
400
|
} else if (variant === "underlined") {
|
|
394
401
|
wrapperStyles.backgroundColor = "transparent";
|
|
@@ -397,8 +404,7 @@ export const Input = (props) => {
|
|
|
397
404
|
} else {
|
|
398
405
|
// Default: outlined
|
|
399
406
|
wrapperStyles.backgroundColor = "transparent";
|
|
400
|
-
wrapperStyles.borderRadius =
|
|
401
|
-
typeof borderRadius === "number" ? `${borderRadius}px` : borderRadius;
|
|
407
|
+
wrapperStyles.borderRadius = toREM(borderRadius);
|
|
402
408
|
wrapperStyles.border = `1px solid ${error ? colors.danger : colors.border}`;
|
|
403
409
|
}
|
|
404
410
|
|
|
@@ -414,7 +420,7 @@ export const Input = (props) => {
|
|
|
414
420
|
name: iconLeft,
|
|
415
421
|
size: iconSize || sz.iconSize,
|
|
416
422
|
color: iconColor,
|
|
417
|
-
marginLeft:
|
|
423
|
+
marginLeft: 12,
|
|
418
424
|
flexShrink: 0,
|
|
419
425
|
pointerEvents: "auto",
|
|
420
426
|
});
|
|
@@ -436,8 +442,9 @@ export const Input = (props) => {
|
|
|
436
442
|
inputElement.value = currentValue;
|
|
437
443
|
|
|
438
444
|
inputElement.style.flex = "1";
|
|
439
|
-
inputElement.style.padding = sz.padding
|
|
440
|
-
inputElement.style.fontSize = sz.fontSize;
|
|
445
|
+
inputElement.style.padding = `${toREM(sz.padding[0])} ${toREM(sz.padding[1])}`;
|
|
446
|
+
inputElement.style.fontSize = toREM(sz.fontSize);
|
|
447
|
+
inputElement.style.minHeight = toREM(sz.minHeight);
|
|
441
448
|
inputElement.style.border = "none";
|
|
442
449
|
inputElement.style.outline = "none";
|
|
443
450
|
inputElement.style.backgroundColor = "transparent";
|
|
@@ -505,15 +512,15 @@ export const Input = (props) => {
|
|
|
505
512
|
btn.type = "button";
|
|
506
513
|
btn.textContent = glyph;
|
|
507
514
|
btn.setAttribute("aria-label", label);
|
|
508
|
-
btn.style.width =
|
|
509
|
-
btn.style.height =
|
|
515
|
+
btn.style.width = toREM(32);
|
|
516
|
+
btn.style.height = toREM(32);
|
|
510
517
|
btn.style.flexShrink = "0";
|
|
511
|
-
btn.style.marginRight =
|
|
518
|
+
btn.style.marginRight = toREM(4);
|
|
512
519
|
btn.style.padding = "0";
|
|
513
520
|
btn.style.border = "none";
|
|
514
521
|
btn.style.background = "transparent";
|
|
515
522
|
btn.style.color = colors.textSecondary;
|
|
516
|
-
btn.style.fontSize =
|
|
523
|
+
btn.style.fontSize = toREM(16);
|
|
517
524
|
btn.style.lineHeight = "1";
|
|
518
525
|
btn.style.cursor = disabled ? "not-allowed" : "pointer";
|
|
519
526
|
btn.style.display = "flex";
|
|
@@ -564,12 +571,12 @@ export const Input = (props) => {
|
|
|
564
571
|
if (showValidationIcon && validation !== "none") {
|
|
565
572
|
validationIcon = document.createElement("span");
|
|
566
573
|
validationIcon.textContent = "";
|
|
567
|
-
validationIcon.style.width =
|
|
568
|
-
validationIcon.style.fontSize =
|
|
574
|
+
validationIcon.style.width = toREM(24);
|
|
575
|
+
validationIcon.style.fontSize = toREM(16);
|
|
569
576
|
validationIcon.style.display = "none";
|
|
570
577
|
validationIcon.style.alignItems = "center";
|
|
571
578
|
validationIcon.style.justifyContent = "center";
|
|
572
|
-
validationIcon.style.marginRight =
|
|
579
|
+
validationIcon.style.marginRight = toREM(8);
|
|
573
580
|
validationIcon.style.flexShrink = "0";
|
|
574
581
|
inputWrapper.appendChild(validationIcon);
|
|
575
582
|
}
|
|
@@ -580,7 +587,7 @@ export const Input = (props) => {
|
|
|
580
587
|
name: iconRight,
|
|
581
588
|
size: iconSize || sz.iconSize,
|
|
582
589
|
color: iconColor,
|
|
583
|
-
marginRight:
|
|
590
|
+
marginRight: 12,
|
|
584
591
|
flexShrink: 0,
|
|
585
592
|
pointerEvents: "auto",
|
|
586
593
|
});
|
|
@@ -593,9 +600,9 @@ export const Input = (props) => {
|
|
|
593
600
|
// --- Error message element rendered below the input ---
|
|
594
601
|
errorMessageElement = document.createElement("span");
|
|
595
602
|
errorMessageElement.textContent = "";
|
|
596
|
-
errorMessageElement.style.fontSize =
|
|
603
|
+
errorMessageElement.style.fontSize = toREM(12);
|
|
597
604
|
errorMessageElement.style.color = colors.danger;
|
|
598
|
-
errorMessageElement.style.marginTop =
|
|
605
|
+
errorMessageElement.style.marginTop = toREM(4);
|
|
599
606
|
errorMessageElement.style.display = "none";
|
|
600
607
|
container.appendChild(errorMessageElement);
|
|
601
608
|
|
package/src/widgets/Inspector.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// utils/Inspector.js
|
|
2
2
|
import { getWidgetProps } from "../utils/getWidgetProps.js";
|
|
3
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
4
|
+
import { colors } from "../utils/themes.js";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Converts a JavaScript value to its source-code string representation.
|
|
@@ -138,6 +140,44 @@ const collectChildren = (widget) => {
|
|
|
138
140
|
return children;
|
|
139
141
|
};
|
|
140
142
|
|
|
143
|
+
/**
|
|
144
|
+
* The widget form: a monospace block holding the reconstructed call.
|
|
145
|
+
*
|
|
146
|
+
* `Inspector` itself is a string builder, which is what the console helpers
|
|
147
|
+
* need. A screen that wants to *show* a tree asks for this one, by passing the
|
|
148
|
+
* props object the documentation describes.
|
|
149
|
+
*
|
|
150
|
+
* @param {{ widget?: HTMLElement, indent?: number }} props
|
|
151
|
+
* @returns {HTMLElement}
|
|
152
|
+
*/
|
|
153
|
+
const InspectorView = ({ widget, indent = 0, ...rest }) => {
|
|
154
|
+
const dump = widget
|
|
155
|
+
? Inspector(widget, indent)
|
|
156
|
+
: 'Inspector({ widget }) — nothing to inspect.';
|
|
157
|
+
|
|
158
|
+
const container = WidgetFactory({
|
|
159
|
+
widgetName: "Inspector",
|
|
160
|
+
tag: "pre",
|
|
161
|
+
text: dump,
|
|
162
|
+
fontFamily:
|
|
163
|
+
'ui-monospace, SFMono-Regular, Menlo, Consolas, "Courier New", monospace',
|
|
164
|
+
fontSize: 12,
|
|
165
|
+
color: colors.text,
|
|
166
|
+
backgroundColor: colors.card,
|
|
167
|
+
padding: 16,
|
|
168
|
+
borderRadius: 8,
|
|
169
|
+
overflowX: "auto",
|
|
170
|
+
width: "100%",
|
|
171
|
+
...rest,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// A <pre> that wraps is a dump nobody can read; the horizontal scroll above
|
|
175
|
+
// is the deal.
|
|
176
|
+
container.style.whiteSpace = "pre";
|
|
177
|
+
|
|
178
|
+
return container;
|
|
179
|
+
};
|
|
180
|
+
|
|
141
181
|
/**
|
|
142
182
|
* Converts a widget element back into its equivalent source-code representation,
|
|
143
183
|
* recursively reconstructing its full prop tree and children.
|
|
@@ -149,7 +189,20 @@ const collectChildren = (widget) => {
|
|
|
149
189
|
* @param {number} [indent=0] - Starting indentation depth for pretty-printing.
|
|
150
190
|
* @returns {string} Source code string such as `Row({ gap: 8, children: [...] })`.
|
|
151
191
|
*/
|
|
152
|
-
export const Inspector = (
|
|
192
|
+
export const Inspector = (target, indent = 0) => {
|
|
193
|
+
// Two calls, one function. `Inspector({ widget, indent })` is the documented
|
|
194
|
+
// widget: it paints the dump so a settings screen can show a tree. Called
|
|
195
|
+
// with a node it stays the string builder `printWidgetCode` uses.
|
|
196
|
+
if (
|
|
197
|
+
target &&
|
|
198
|
+
typeof target === "object" &&
|
|
199
|
+
target.nodeType !== 1 &&
|
|
200
|
+
"widget" in target
|
|
201
|
+
) {
|
|
202
|
+
return InspectorView(target);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const widget = target;
|
|
153
206
|
if (!widget || widget.nodeType !== 1) {
|
|
154
207
|
return String(widget);
|
|
155
208
|
}
|
|
@@ -10,9 +10,11 @@ export const InstallButton = (props) => {
|
|
|
10
10
|
text = "📲 Install",
|
|
11
11
|
variant = "filled",
|
|
12
12
|
size = "medium",
|
|
13
|
-
borderRadius =
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
borderRadius = 16,
|
|
14
|
+
// A number is the framework's own form: Button doubles it horizontally
|
|
15
|
+
// (12 vertical, 24 horizontal) and the factory emits it as `rem`.
|
|
16
|
+
padding = 12,
|
|
17
|
+
bottom = 24,
|
|
16
18
|
backgroundColor,
|
|
17
19
|
color,
|
|
18
20
|
onInstalled,
|
|
@@ -64,7 +66,7 @@ export const InstallButton = (props) => {
|
|
|
64
66
|
borderRadius: borderRadius,
|
|
65
67
|
padding: padding,
|
|
66
68
|
position: "fixed",
|
|
67
|
-
bottom
|
|
69
|
+
bottom,
|
|
68
70
|
left: "50%",
|
|
69
71
|
transform: "translateX(-50%)",
|
|
70
72
|
zIndex: 10000,
|
package/src/widgets/ListTile.js
CHANGED
|
@@ -7,6 +7,7 @@ import { Row } from "./Row.js";
|
|
|
7
7
|
import { Column } from "./Column.js";
|
|
8
8
|
import { Text } from "./Text.js";
|
|
9
9
|
import { colors, subscribeTheme } from "../utils/themes.js";
|
|
10
|
+
import { toREM } from "../utils/units.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* @typedef {Object} ListTileProps
|
|
@@ -132,7 +133,7 @@ export const ListTile = (props) => {
|
|
|
132
133
|
const contentRow = Row({
|
|
133
134
|
alignItems: "center",
|
|
134
135
|
gap: gap,
|
|
135
|
-
padding: `${paddingVertical}
|
|
136
|
+
padding: `${toREM(paddingVertical)} ${toREM(paddingHorizontal)}`,
|
|
136
137
|
width: "100%",
|
|
137
138
|
boxSizing: "border-box",
|
|
138
139
|
children: rowChildren,
|
|
@@ -199,8 +200,8 @@ export const ListTile = (props) => {
|
|
|
199
200
|
const dividerLine = WidgetFactory({
|
|
200
201
|
height: 1,
|
|
201
202
|
backgroundColor: colors.border,
|
|
202
|
-
marginLeft:
|
|
203
|
-
width: `calc(100% - ${paddingHorizontal}
|
|
203
|
+
marginLeft: toREM(paddingHorizontal),
|
|
204
|
+
width: `calc(100% - ${toREM(paddingHorizontal)})`,
|
|
204
205
|
});
|
|
205
206
|
|
|
206
207
|
const container = WidgetFactory({
|