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,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Radio.js
|
|
3
|
+
* @description Radio input with controlled/uncontrolled selection state.
|
|
4
|
+
*/
|
|
5
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
|
+
import { colors } from "../utils/themes.js";
|
|
7
|
+
|
|
8
|
+
export const Radio = (props) => {
|
|
9
|
+
const {
|
|
10
|
+
selected = false,
|
|
11
|
+
onSelect,
|
|
12
|
+
onPress: onPressProp,
|
|
13
|
+
disabled = false,
|
|
14
|
+
size = 20,
|
|
15
|
+
name,
|
|
16
|
+
...rest
|
|
17
|
+
} = props;
|
|
18
|
+
|
|
19
|
+
/** @deprecated Use onPress instead */
|
|
20
|
+
// onSelect kept as alias for backward compatibility
|
|
21
|
+
const onPress = onPressProp || onSelect;
|
|
22
|
+
|
|
23
|
+
let internalSelected = selected;
|
|
24
|
+
let radioRef = null;
|
|
25
|
+
let dotRef = null;
|
|
26
|
+
|
|
27
|
+
const updateUI = (newSelected) => {
|
|
28
|
+
if (radioRef) {
|
|
29
|
+
radioRef.style.backgroundColor = newSelected
|
|
30
|
+
? colors.primary
|
|
31
|
+
: "transparent";
|
|
32
|
+
radioRef.style.border = `2px solid ${newSelected ? colors.primary : colors.border}`;
|
|
33
|
+
}
|
|
34
|
+
if (dotRef) {
|
|
35
|
+
dotRef.style.display = newSelected ? "block" : "none";
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Createste dot
|
|
40
|
+
const dot = WidgetFactory({
|
|
41
|
+
width: size / 2,
|
|
42
|
+
height: size / 2,
|
|
43
|
+
backgroundColor: "#fff",
|
|
44
|
+
borderRadius: "50%",
|
|
45
|
+
display: internalSelected ? "block" : "none",
|
|
46
|
+
});
|
|
47
|
+
dotRef = dot;
|
|
48
|
+
|
|
49
|
+
// Createste main radio container
|
|
50
|
+
const radio = WidgetFactory({
|
|
51
|
+
widgetName: "Radio",
|
|
52
|
+
width: size,
|
|
53
|
+
height: size,
|
|
54
|
+
border: `2px solid ${disabled ? colors.border : internalSelected ? colors.primary : colors.border}`,
|
|
55
|
+
borderRadius: "50%",
|
|
56
|
+
backgroundColor: internalSelected ? colors.primary : "transparent",
|
|
57
|
+
display: "flex",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
transition: "all 0.2s ease",
|
|
61
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
62
|
+
opacity: disabled ? 0.5 : 1,
|
|
63
|
+
child: dot,
|
|
64
|
+
...rest,
|
|
65
|
+
});
|
|
66
|
+
radioRef = radio;
|
|
67
|
+
|
|
68
|
+
// Handle click
|
|
69
|
+
if (!disabled) {
|
|
70
|
+
radio.onclick = (e) => {
|
|
71
|
+
e.stopPropagation();
|
|
72
|
+
if (internalSelected) return;
|
|
73
|
+
|
|
74
|
+
const newSelected = true;
|
|
75
|
+
internalSelected = newSelected;
|
|
76
|
+
updateUI(newSelected);
|
|
77
|
+
|
|
78
|
+
if (name) {
|
|
79
|
+
const allRadios = document.querySelectorAll(
|
|
80
|
+
`[data-radio-group="${name}"]`,
|
|
81
|
+
);
|
|
82
|
+
allRadios.forEach((otherRadio) => {
|
|
83
|
+
if (otherRadio !== radio && otherRadio.unselect) {
|
|
84
|
+
otherRadio.unselect();
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (onPress) onPress(true);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Hover effect
|
|
93
|
+
radio.addEventListener("mouseenter", () => {
|
|
94
|
+
if (!disabled && !internalSelected) {
|
|
95
|
+
radio.style.backgroundColor = colors.primary + "20";
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
radio.addEventListener("mouseleave", () => {
|
|
100
|
+
if (!disabled && !internalSelected) {
|
|
101
|
+
radio.style.backgroundColor = "transparent";
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Public methods
|
|
107
|
+
radio.select = () => {
|
|
108
|
+
if (!disabled && !internalSelected) {
|
|
109
|
+
internalSelected = true;
|
|
110
|
+
updateUI(true);
|
|
111
|
+
if (onPress) onPress(true);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
radio.unselect = () => {
|
|
116
|
+
if (!disabled && internalSelected) {
|
|
117
|
+
internalSelected = false;
|
|
118
|
+
updateUI(false);
|
|
119
|
+
if (onPress) onPress(false);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
radio.isSelected = () => internalSelected;
|
|
124
|
+
|
|
125
|
+
if (name) {
|
|
126
|
+
radio.setAttribute("data-radio-group", name);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return radio;
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default Radio;
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Rating.js
|
|
3
|
+
* @description Star rating supporting mouse, touch and swipe interactions.
|
|
4
|
+
*/
|
|
5
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
|
+
import { Row } from "./Row.js";
|
|
7
|
+
import { Text } from "./Text.js";
|
|
8
|
+
import { Icon } from "./Icon.js";
|
|
9
|
+
import { colors, subscribeTheme } from "../utils/themes.js";
|
|
10
|
+
|
|
11
|
+
export const Rating = (props) => {
|
|
12
|
+
const {
|
|
13
|
+
value = 0,
|
|
14
|
+
max = 5,
|
|
15
|
+
onChange,
|
|
16
|
+
onPress,
|
|
17
|
+
readOnly = false,
|
|
18
|
+
size = 20,
|
|
19
|
+
activeColor: propActiveColor,
|
|
20
|
+
inactiveColor: propInactiveColor,
|
|
21
|
+
iconActive = "star",
|
|
22
|
+
iconInactive = "star_border",
|
|
23
|
+
iconHalf = "star_half",
|
|
24
|
+
gap = 2,
|
|
25
|
+
allowHalf = false,
|
|
26
|
+
showValue = false,
|
|
27
|
+
valueColor = colors.textSecondary,
|
|
28
|
+
valueSize,
|
|
29
|
+
...rest
|
|
30
|
+
} = props;
|
|
31
|
+
|
|
32
|
+
// Colors from theme
|
|
33
|
+
let activeColor =
|
|
34
|
+
propActiveColor !== undefined ? propActiveColor : colors.warning;
|
|
35
|
+
let inactiveColor =
|
|
36
|
+
propInactiveColor !== undefined ? propInactiveColor : colors.border;
|
|
37
|
+
let currentValue = Math.min(Math.max(value, 0), max);
|
|
38
|
+
|
|
39
|
+
let starIcons = []; // stores the SPAN elements of each star
|
|
40
|
+
let starWrappers = []; // stores the wrappers (for events)
|
|
41
|
+
let valueTextRef = null;
|
|
42
|
+
let unsubscribeTheme = null;
|
|
43
|
+
let touchActive = false; // to avoid conflicts with mouse
|
|
44
|
+
|
|
45
|
+
// Update all stars according to currentValue
|
|
46
|
+
const updateStars = () => {
|
|
47
|
+
for (let i = 0; i < max; i++) {
|
|
48
|
+
const starNumber = i + 1;
|
|
49
|
+
const span = starIcons[i];
|
|
50
|
+
if (!span) continue;
|
|
51
|
+
|
|
52
|
+
let iconName = iconInactive;
|
|
53
|
+
let color = inactiveColor;
|
|
54
|
+
|
|
55
|
+
if (starNumber <= currentValue) {
|
|
56
|
+
iconName = iconActive;
|
|
57
|
+
color = activeColor;
|
|
58
|
+
} else if (allowHalf && starNumber - 0.5 === currentValue) {
|
|
59
|
+
iconName = iconHalf;
|
|
60
|
+
color = activeColor;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
span.textContent = iconName;
|
|
64
|
+
span.style.color = color;
|
|
65
|
+
}
|
|
66
|
+
if (valueTextRef && showValue) {
|
|
67
|
+
valueTextRef.update({ text: currentValue.toFixed(1) });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Change the actual value (persistent)
|
|
72
|
+
const setValue = (newValue) => {
|
|
73
|
+
if (readOnly) return;
|
|
74
|
+
let clamped = Math.min(Math.max(newValue, 0), max);
|
|
75
|
+
if (clamped === currentValue) return;
|
|
76
|
+
currentValue = clamped;
|
|
77
|
+
updateStars();
|
|
78
|
+
const cb = onChange || onPress;
|
|
79
|
+
if (cb) cb(currentValue);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Temporary preview (hover or touchmove)
|
|
83
|
+
const previewValue = (starIndex) => {
|
|
84
|
+
for (let i = 0; i < max; i++) {
|
|
85
|
+
const span = starIcons[i];
|
|
86
|
+
if (!span) continue;
|
|
87
|
+
if (i <= starIndex) {
|
|
88
|
+
span.textContent = iconActive;
|
|
89
|
+
span.style.color = activeColor;
|
|
90
|
+
} else {
|
|
91
|
+
span.textContent = iconInactive;
|
|
92
|
+
span.style.color = inactiveColor;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Restore the display according to the actual value
|
|
98
|
+
const restoreFromPreview = () => {
|
|
99
|
+
updateStars();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// ========== STAR CREATION ==========
|
|
103
|
+
for (let i = 0; i < max; i++) {
|
|
104
|
+
const icon = Icon({
|
|
105
|
+
name: iconInactive,
|
|
106
|
+
size: size,
|
|
107
|
+
color: inactiveColor,
|
|
108
|
+
});
|
|
109
|
+
const wrapper = WidgetFactory({
|
|
110
|
+
display: "inline-flex",
|
|
111
|
+
cursor: readOnly ? "default" : "pointer",
|
|
112
|
+
transition: "transform 0.1s ease",
|
|
113
|
+
child: icon,
|
|
114
|
+
padding: "4px", // larger touch area
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
starIcons.push(icon);
|
|
118
|
+
starWrappers.push(wrapper);
|
|
119
|
+
|
|
120
|
+
if (!readOnly) {
|
|
121
|
+
// ----- MOUSE EVENTS -----
|
|
122
|
+
wrapper.addEventListener("mouseenter", () => {
|
|
123
|
+
if (touchActive) return; // avoid conflict with touch
|
|
124
|
+
wrapper.style.transform = "scale(1.15)";
|
|
125
|
+
previewValue(i);
|
|
126
|
+
});
|
|
127
|
+
wrapper.addEventListener("mouseleave", () => {
|
|
128
|
+
if (touchActive) return;
|
|
129
|
+
wrapper.style.transform = "scale(1)";
|
|
130
|
+
restoreFromPreview();
|
|
131
|
+
});
|
|
132
|
+
wrapper.addEventListener("click", () => {
|
|
133
|
+
if (touchActive) return;
|
|
134
|
+
let newValue = i + 1;
|
|
135
|
+
if (allowHalf) {
|
|
136
|
+
if (currentValue === newValue) newValue = newValue - 0.5;
|
|
137
|
+
else if (currentValue === newValue - 0.5) newValue = newValue;
|
|
138
|
+
else newValue = newValue;
|
|
139
|
+
}
|
|
140
|
+
setValue(newValue);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// ----- TOUCH EVENTS -----
|
|
144
|
+
wrapper.addEventListener("touchstart", (e) => {
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
touchActive = true;
|
|
147
|
+
wrapper.style.transform = "scale(1.15)";
|
|
148
|
+
previewValue(i);
|
|
149
|
+
});
|
|
150
|
+
wrapper.addEventListener("touchmove", (e) => {
|
|
151
|
+
e.preventDefault();
|
|
152
|
+
const touch = e.touches[0];
|
|
153
|
+
const elemUnderTouch = document.elementFromPoint(
|
|
154
|
+
touch.clientX,
|
|
155
|
+
touch.clientY,
|
|
156
|
+
);
|
|
157
|
+
let targetIndex = -1;
|
|
158
|
+
for (let idx = 0; idx < starWrappers.length; idx++) {
|
|
159
|
+
if (starWrappers[idx].contains(elemUnderTouch)) {
|
|
160
|
+
targetIndex = idx;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (targetIndex !== -1 && targetIndex !== i) {
|
|
165
|
+
previewValue(targetIndex);
|
|
166
|
+
} else if (targetIndex === -1) {
|
|
167
|
+
restoreFromPreview();
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
wrapper.addEventListener("touchend", (e) => {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
wrapper.style.transform = "scale(1)";
|
|
173
|
+
// Determine which star is currently previewed (the one with iconActive)
|
|
174
|
+
let selectedIndex = -1;
|
|
175
|
+
for (let idx = 0; idx < starIcons.length; idx++) {
|
|
176
|
+
if (starIcons[idx].textContent === iconActive) {
|
|
177
|
+
selectedIndex = idx;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (selectedIndex !== -1) {
|
|
182
|
+
let newValue = selectedIndex + 1;
|
|
183
|
+
if (allowHalf) {
|
|
184
|
+
// For touch, we always consider the integer; if you want half a star, it can be adjusted
|
|
185
|
+
}
|
|
186
|
+
setValue(newValue);
|
|
187
|
+
} else {
|
|
188
|
+
restoreFromPreview();
|
|
189
|
+
}
|
|
190
|
+
touchActive = false;
|
|
191
|
+
});
|
|
192
|
+
wrapper.addEventListener("touchcancel", (e) => {
|
|
193
|
+
wrapper.style.transform = "scale(1)";
|
|
194
|
+
restoreFromPreview();
|
|
195
|
+
touchActive = false;
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Stars container
|
|
201
|
+
const starsContainer = Row({
|
|
202
|
+
alignItems: "center",
|
|
203
|
+
gap: gap,
|
|
204
|
+
flexWrap: "wrap",
|
|
205
|
+
children: starWrappers,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Ensure initial state
|
|
209
|
+
updateStars();
|
|
210
|
+
|
|
211
|
+
// Final construction
|
|
212
|
+
const children = [starsContainer];
|
|
213
|
+
if (showValue) {
|
|
214
|
+
const valueText = Text({
|
|
215
|
+
text: currentValue.toFixed(1),
|
|
216
|
+
size: valueSize || size * 0.7,
|
|
217
|
+
color: valueColor,
|
|
218
|
+
weight: "bold",
|
|
219
|
+
minWidth: "40px",
|
|
220
|
+
textAlign: "center",
|
|
221
|
+
});
|
|
222
|
+
valueTextRef = valueText;
|
|
223
|
+
children.push(valueText);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const ratingContainer = WidgetFactory({
|
|
227
|
+
widgetName: "Rating",
|
|
228
|
+
display: "inline-flex",
|
|
229
|
+
alignItems: "center",
|
|
230
|
+
gap: gap * 2,
|
|
231
|
+
children: children,
|
|
232
|
+
...rest,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Dynamic theme
|
|
236
|
+
if (propActiveColor === undefined || propInactiveColor === undefined) {
|
|
237
|
+
unsubscribeTheme = subscribeTheme(() => {
|
|
238
|
+
if (propActiveColor === undefined) activeColor = colors.warning;
|
|
239
|
+
if (propInactiveColor === undefined) inactiveColor = colors.border;
|
|
240
|
+
updateStars();
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
ratingContainer.onUnmount(() => {
|
|
245
|
+
if (unsubscribeTheme) unsubscribeTheme();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
ratingContainer.setValue = setValue;
|
|
249
|
+
ratingContainer.getValue = () => currentValue;
|
|
250
|
+
ratingContainer.updateStars = updateStars;
|
|
251
|
+
|
|
252
|
+
Object.defineProperty(ratingContainer, "value", {
|
|
253
|
+
get: () => currentValue,
|
|
254
|
+
set: (newValue) => setValue(newValue),
|
|
255
|
+
enumerable: true,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return ratingContainer;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export default Rating;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Row.js
|
|
3
|
+
* @description Horizontal flex layout container (default `flex-direction: row`).
|
|
4
|
+
*/
|
|
5
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
|
+
|
|
7
|
+
export const Row = (props = {}) => {
|
|
8
|
+
const {
|
|
9
|
+
display = "flex",
|
|
10
|
+
flexDirection = "row",
|
|
11
|
+
width = "100%",
|
|
12
|
+
height = "auto",
|
|
13
|
+
boxSizing = "border-box",
|
|
14
|
+
...rest
|
|
15
|
+
} = props;
|
|
16
|
+
|
|
17
|
+
return WidgetFactory({
|
|
18
|
+
tag: "div",
|
|
19
|
+
widgetName: "Row",
|
|
20
|
+
display: display,
|
|
21
|
+
flexDirection: flexDirection,
|
|
22
|
+
width: width,
|
|
23
|
+
height: height,
|
|
24
|
+
boxSizing: boxSizing,
|
|
25
|
+
...rest,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default Row;
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Skeleton.js
|
|
3
|
+
* @description Loading placeholder (pulse/wave) using globally injected keyframes.
|
|
4
|
+
*/
|
|
5
|
+
import { WidgetFactory } from "../widget-factory/index.js";
|
|
6
|
+
import { colors } from "../utils/themes.js";
|
|
7
|
+
import { injectKeyframes } from "../utils/styleInjector.js";
|
|
8
|
+
|
|
9
|
+
export const Skeleton = (props) => {
|
|
10
|
+
const {
|
|
11
|
+
variant = "text", // 'text', 'circular', 'avatar', 'image', 'card', 'listTile', 'button'
|
|
12
|
+
width,
|
|
13
|
+
height,
|
|
14
|
+
borderRadius,
|
|
15
|
+
animation = "pulse", // 'pulse', 'wave', 'none'
|
|
16
|
+
count = 1,
|
|
17
|
+
gap = 8,
|
|
18
|
+
|
|
19
|
+
// Color props for consistency
|
|
20
|
+
bgColor = colors.gray200,
|
|
21
|
+
highlightColor = colors.gray100,
|
|
22
|
+
shimmerColor = colors.gray300,
|
|
23
|
+
|
|
24
|
+
// Animation timing
|
|
25
|
+
pulseDuration = "1.5s",
|
|
26
|
+
waveDuration = "1.5s",
|
|
27
|
+
|
|
28
|
+
...rest
|
|
29
|
+
} = props;
|
|
30
|
+
|
|
31
|
+
// Default dimensions based on variant
|
|
32
|
+
const getDefaultDimensions = () => {
|
|
33
|
+
switch (variant) {
|
|
34
|
+
case "text":
|
|
35
|
+
return { width: "100%", height: 16, borderRadius: 4 };
|
|
36
|
+
case "circular":
|
|
37
|
+
return { width: 48, height: 48, borderRadius: "50%" };
|
|
38
|
+
case "avatar":
|
|
39
|
+
return { width: 40, height: 40, borderRadius: "50%" };
|
|
40
|
+
case "image":
|
|
41
|
+
return { width: "100%", height: 150, borderRadius: 8 };
|
|
42
|
+
case "card":
|
|
43
|
+
return { width: "100%", height: 120, borderRadius: 12 };
|
|
44
|
+
case "listTile":
|
|
45
|
+
return { width: "100%", height: 56, borderRadius: 8 };
|
|
46
|
+
case "button":
|
|
47
|
+
return { width: 120, height: 36, borderRadius: 24 };
|
|
48
|
+
default:
|
|
49
|
+
return { width: "100%", height: 20, borderRadius: 4 };
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const defaults = getDefaultDimensions();
|
|
54
|
+
const finalWidth = width !== undefined ? width : defaults.width;
|
|
55
|
+
const finalHeight = height !== undefined ? height : defaults.height;
|
|
56
|
+
const finalBorderRadius =
|
|
57
|
+
borderRadius !== undefined ? borderRadius : defaults.borderRadius;
|
|
58
|
+
|
|
59
|
+
// Animation styles
|
|
60
|
+
const getAnimationStyle = () => {
|
|
61
|
+
if (animation === "pulse") {
|
|
62
|
+
return {
|
|
63
|
+
animation: `skeleton-pulse ${pulseDuration} ease-in-out infinite`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (animation === "wave") {
|
|
67
|
+
return {
|
|
68
|
+
background: `linear-gradient(90deg, ${bgColor} 25%, ${highlightColor} 50%, ${bgColor} 75%)`,
|
|
69
|
+
backgroundSize: "200% 100%",
|
|
70
|
+
animation: `skeleton-wave ${waveDuration} ease-in-out infinite`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {};
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Build specific variant structure using WidgetFactory
|
|
77
|
+
const buildVariant = () => {
|
|
78
|
+
// Button variant
|
|
79
|
+
if (variant === "button") {
|
|
80
|
+
return WidgetFactory({
|
|
81
|
+
tag: "div",
|
|
82
|
+
width: typeof finalWidth === "number" ? `${finalWidth}px` : finalWidth,
|
|
83
|
+
height:
|
|
84
|
+
typeof finalHeight === "number" ? `${finalHeight}px` : finalHeight,
|
|
85
|
+
backgroundColor: bgColor,
|
|
86
|
+
borderRadius:
|
|
87
|
+
typeof finalBorderRadius === "number"
|
|
88
|
+
? `${finalBorderRadius}px`
|
|
89
|
+
: finalBorderRadius,
|
|
90
|
+
...getAnimationStyle(),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ListTile variant
|
|
95
|
+
if (variant === "listTile") {
|
|
96
|
+
const container = WidgetFactory({
|
|
97
|
+
tag: "div",
|
|
98
|
+
display: "flex",
|
|
99
|
+
alignItems: "center",
|
|
100
|
+
gap: "12px",
|
|
101
|
+
width: "100%",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const avatar = WidgetFactory({
|
|
105
|
+
tag: "div",
|
|
106
|
+
width: "40px",
|
|
107
|
+
height: "40px",
|
|
108
|
+
backgroundColor: bgColor,
|
|
109
|
+
borderRadius: "50%",
|
|
110
|
+
flexShrink: 0,
|
|
111
|
+
...getAnimationStyle(),
|
|
112
|
+
});
|
|
113
|
+
container.appendChild(avatar);
|
|
114
|
+
|
|
115
|
+
const textContainer = WidgetFactory({
|
|
116
|
+
tag: "div",
|
|
117
|
+
flex: 1,
|
|
118
|
+
display: "flex",
|
|
119
|
+
flexDirection: "column",
|
|
120
|
+
gap: "8px",
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const line1 = WidgetFactory({
|
|
124
|
+
tag: "div",
|
|
125
|
+
width: "80%",
|
|
126
|
+
height: "14px",
|
|
127
|
+
backgroundColor: bgColor,
|
|
128
|
+
borderRadius: "4px",
|
|
129
|
+
...getAnimationStyle(),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const line2 = WidgetFactory({
|
|
133
|
+
tag: "div",
|
|
134
|
+
width: "60%",
|
|
135
|
+
height: "12px",
|
|
136
|
+
backgroundColor: bgColor,
|
|
137
|
+
borderRadius: "4px",
|
|
138
|
+
...getAnimationStyle(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
textContainer.appendChild(line1);
|
|
142
|
+
textContainer.appendChild(line2);
|
|
143
|
+
container.appendChild(textContainer);
|
|
144
|
+
|
|
145
|
+
return container;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Card variant
|
|
149
|
+
if (variant === "card") {
|
|
150
|
+
const container = WidgetFactory({
|
|
151
|
+
tag: "div",
|
|
152
|
+
display: "flex",
|
|
153
|
+
flexDirection: "column",
|
|
154
|
+
gap: "12px",
|
|
155
|
+
width: "100%",
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const imageArea = WidgetFactory({
|
|
159
|
+
tag: "div",
|
|
160
|
+
width: "100%",
|
|
161
|
+
height: "120px",
|
|
162
|
+
backgroundColor: bgColor,
|
|
163
|
+
borderRadius: "8px",
|
|
164
|
+
...getAnimationStyle(),
|
|
165
|
+
});
|
|
166
|
+
container.appendChild(imageArea);
|
|
167
|
+
|
|
168
|
+
const textLines = WidgetFactory({
|
|
169
|
+
tag: "div",
|
|
170
|
+
display: "flex",
|
|
171
|
+
flexDirection: "column",
|
|
172
|
+
gap: "8px",
|
|
173
|
+
padding: "0 8px",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const line1 = WidgetFactory({
|
|
177
|
+
tag: "div",
|
|
178
|
+
width: "90%",
|
|
179
|
+
height: "16px",
|
|
180
|
+
backgroundColor: bgColor,
|
|
181
|
+
borderRadius: "4px",
|
|
182
|
+
...getAnimationStyle(),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
const line2 = WidgetFactory({
|
|
186
|
+
tag: "div",
|
|
187
|
+
width: "70%",
|
|
188
|
+
height: "12px",
|
|
189
|
+
backgroundColor: bgColor,
|
|
190
|
+
borderRadius: "4px",
|
|
191
|
+
...getAnimationStyle(),
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
textLines.appendChild(line1);
|
|
195
|
+
textLines.appendChild(line2);
|
|
196
|
+
container.appendChild(textLines);
|
|
197
|
+
|
|
198
|
+
return container;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Simple variants (text, circular, avatar, image)
|
|
202
|
+
return WidgetFactory({
|
|
203
|
+
tag: "div",
|
|
204
|
+
width: typeof finalWidth === "number" ? `${finalWidth}px` : finalWidth,
|
|
205
|
+
height:
|
|
206
|
+
typeof finalHeight === "number" ? `${finalHeight}px` : finalHeight,
|
|
207
|
+
backgroundColor: bgColor,
|
|
208
|
+
borderRadius:
|
|
209
|
+
typeof finalBorderRadius === "number"
|
|
210
|
+
? `${finalBorderRadius}px`
|
|
211
|
+
: finalBorderRadius,
|
|
212
|
+
...getAnimationStyle(),
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Createste multiple items container using WidgetFactory
|
|
217
|
+
const container = WidgetFactory({
|
|
218
|
+
tag: "div",
|
|
219
|
+
widgetName: "Skeleton",
|
|
220
|
+
display: "flex",
|
|
221
|
+
flexDirection: "column",
|
|
222
|
+
gap: typeof gap === "number" ? `${gap}px` : gap,
|
|
223
|
+
width: "100%",
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
for (let i = 0; i < count; i++) {
|
|
227
|
+
container.appendChild(buildVariant());
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
injectKeyframes(
|
|
231
|
+
"skeleton-pulse",
|
|
232
|
+
"0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; }",
|
|
233
|
+
"skeleton-styles",
|
|
234
|
+
);
|
|
235
|
+
injectKeyframes(
|
|
236
|
+
"skeleton-wave",
|
|
237
|
+
"0% { background-position: 200% 0; } 100% { background-position: -200% 0; }",
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
return container;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export default Skeleton;
|