flet-box 1.0.5 → 1.0.7
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 +28 -1
- package/bin/cli.js +85 -18
- package/bin/commands/auditFix.js +682 -0
- package/bin/commands/checkApi.js +7 -5
- package/bin/commands/create.js +40 -10
- package/bin/commands/createBundle.js +54 -1
- package/bin/commands/images.js +349 -0
- package/bin/commands/pruneAssets.js +170 -0
- package/bin/commands/release.js +34 -1
- package/bin/commands/runCloudflare.js +47 -9
- package/bin/commands/runServer.js +81 -24
- package/bin/commands/toPng.js +346 -0
- package/bin/commands/unpack.js +1173 -0
- package/bin/ota/loader.js +90 -0
- package/bin/utils/appSources.js +101 -0
- package/bin/utils/bundleConfig.js +166 -0
- package/bin/utils/dtsProps.js +147 -0
- package/bin/utils/imageTools.js +271 -0
- package/bin/utils/images/bmp.js +99 -0
- package/bin/utils/images/gif.js +244 -0
- package/bin/utils/images/index.js +163 -0
- package/bin/utils/images/jpeg.js +643 -0
- package/bin/utils/images/png.js +486 -0
- package/bin/utils/images/unsupported.js +29 -0
- package/bin/utils/neutralise.js +109 -6
- package/bin/utils/templates.js +307 -64
- package/docs/cli/README.md +82 -0
- package/docs/guides/online-updates.md +57 -9
- package/docs/widget/Accordion.md +1 -0
- package/docs/widget/AdMob.md +1 -0
- package/docs/widget/AdSense.md +1 -0
- package/docs/widget/AnimatedBox.md +1 -0
- package/docs/widget/AnimatedText.md +1 -0
- package/docs/widget/Audio.md +2 -1
- package/docs/widget/Avatar.md +12 -3
- package/docs/widget/Badge.md +1 -0
- package/docs/widget/Button.md +1 -0
- package/docs/widget/Card.md +1 -0
- package/docs/widget/Carousel.md +1 -0
- package/docs/widget/Chart.md +1 -0
- package/docs/widget/Checkbox.md +1 -0
- package/docs/widget/Chip.md +1 -0
- package/docs/widget/CircularBar.md +1 -0
- package/docs/widget/CircularChart.md +1 -0
- package/docs/widget/CodeViewer.md +1 -0
- package/docs/widget/Column.md +1 -0
- package/docs/widget/Container.md +1 -0
- package/docs/widget/DataTable.md +1 -0
- package/docs/widget/Divider.md +1 -0
- package/docs/widget/DraggBox.md +3 -2
- package/docs/widget/Dropdown.md +3 -0
- package/docs/widget/DroppBox.md +2 -1
- package/docs/widget/FloatingActionButton.md +1 -0
- package/docs/widget/GridView.md +1 -0
- package/docs/widget/Icon.md +1 -0
- package/docs/widget/Image.md +11 -3
- package/docs/widget/Input.md +1 -0
- package/docs/widget/InstallButton.md +1 -0
- package/docs/widget/ListTile.md +1 -0
- package/docs/widget/ListView.md +1 -0
- package/docs/widget/Maps.md +188 -0
- package/docs/widget/Markdown.md +1 -0
- package/docs/widget/MatrixRain.md +1 -0
- package/docs/widget/Pagination.md +1 -0
- package/docs/widget/ParallaxBox.md +1 -0
- package/docs/widget/ProgressBar.md +1 -0
- package/docs/widget/QRCode.md +1 -0
- package/docs/widget/README.md +12 -10
- package/docs/widget/Radio.md +1 -0
- package/docs/widget/Rating.md +1 -0
- package/docs/widget/Row.md +1 -0
- package/docs/widget/Skeleton.md +1 -0
- package/docs/widget/Slider.md +1 -0
- package/docs/widget/Stack.md +1 -0
- package/docs/widget/Stepper.md +1 -0
- package/docs/widget/Switch.md +1 -0
- package/docs/widget/Text.md +1 -0
- package/docs/widget/Tooltip.md +11 -13
- package/docs/widget/TreeView.md +1 -0
- package/docs/widget/Video.md +3 -2
- package/package.json +2 -2
- package/src/core/hmr-client.js +129 -34
- package/src/index.d.ts +132 -0
- package/src/index.js +1 -0
- package/src/navigations/AppBar.js +15 -4
- package/src/navigations/Drawer.js +9 -1
- package/src/widget-factory/translateProps.js +32 -1
- package/src/widgets/Avatar.js +228 -131
- package/src/widgets/Container.js +13 -0
- package/src/widgets/Dropdown.js +33 -1
- package/src/widgets/Image.js +102 -3
- package/src/widgets/Maps.js +1495 -0
- package/src/widgets/Tooltip.js +12 -2
- package/src/widgets/index.js +1 -0
package/src/core/hmr-client.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/core/hmr-client.js
|
|
2
|
+
import { getAllRam, saveRam } from "../services/RamStore.js";
|
|
2
3
|
|
|
3
4
|
/** @type {WebSocket|null} Active WebSocket connection to the HMR server. */
|
|
4
5
|
let ws = null;
|
|
@@ -11,6 +12,83 @@ let ws = null;
|
|
|
11
12
|
*/
|
|
12
13
|
let modules = new Map();
|
|
13
14
|
|
|
15
|
+
/** sessionStorage key that carries `useState` RAM across a hot reload. */
|
|
16
|
+
const STATE_KEY = "__fletbox_hmr_state";
|
|
17
|
+
|
|
18
|
+
/** How many seconds to keep retrying after the server drops the socket. */
|
|
19
|
+
const MAX_RETRIES = 30;
|
|
20
|
+
|
|
21
|
+
let retries = 0;
|
|
22
|
+
let everOpened = false;
|
|
23
|
+
let retryTimer = null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The live state, JSON-safe.
|
|
27
|
+
*
|
|
28
|
+
* `useState` lives in a plain Map, so a value that cannot cross a page reload
|
|
29
|
+
* (a DOM node, a circular graph, a function) is skipped rather than poisoning
|
|
30
|
+
* the whole snapshot.
|
|
31
|
+
*
|
|
32
|
+
* @returns {Object}
|
|
33
|
+
*/
|
|
34
|
+
const snapshotRam = () => {
|
|
35
|
+
const snapshot = {};
|
|
36
|
+
for (const [key, value] of Object.entries(getAllRam())) {
|
|
37
|
+
try {
|
|
38
|
+
JSON.stringify(value);
|
|
39
|
+
snapshot[key] = value;
|
|
40
|
+
} catch (e) {
|
|
41
|
+
// Not serialisable: this key starts from its default on the next page.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return snapshot;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Reload the page without losing the developer's place: state is written to
|
|
49
|
+
* sessionStorage first and put back by `initHMR` on the next load.
|
|
50
|
+
*
|
|
51
|
+
* @returns {void}
|
|
52
|
+
*/
|
|
53
|
+
const reloadKeepingState = () => {
|
|
54
|
+
try {
|
|
55
|
+
sessionStorage.setItem(STATE_KEY, JSON.stringify(snapshotRam()));
|
|
56
|
+
} catch (e) {
|
|
57
|
+
// Private mode, quota, no sessionStorage: reload anyway.
|
|
58
|
+
}
|
|
59
|
+
window.location.reload();
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Push a previous snapshot back into the RAM store.
|
|
64
|
+
*
|
|
65
|
+
* Runs after the app module has already rendered, which is fine: `saveRam`
|
|
66
|
+
* notifies subscribers, so every `useState` binding repaints with the value it
|
|
67
|
+
* had before the reload.
|
|
68
|
+
*
|
|
69
|
+
* @returns {number} keys restored
|
|
70
|
+
*/
|
|
71
|
+
const restoreRam = () => {
|
|
72
|
+
let raw = null;
|
|
73
|
+
try {
|
|
74
|
+
raw = sessionStorage.getItem(STATE_KEY);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
if (!raw) return 0;
|
|
79
|
+
let saved;
|
|
80
|
+
try {
|
|
81
|
+
saved = JSON.parse(raw);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
let restored = 0;
|
|
86
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
87
|
+
if (saveRam(key, value)) restored++;
|
|
88
|
+
}
|
|
89
|
+
return restored;
|
|
90
|
+
};
|
|
91
|
+
|
|
14
92
|
/**
|
|
15
93
|
* Initialises the Hot Module Replacement (HMR) client.
|
|
16
94
|
*
|
|
@@ -23,19 +101,44 @@ let modules = new Map();
|
|
|
23
101
|
* - `hmr` — a file changed; delegates to `handleHMR`.
|
|
24
102
|
* - `reload` — the server requests a full page reload.
|
|
25
103
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
104
|
+
* A dropped socket is retried, not reloaded: reloading into a server that is
|
|
105
|
+
* still restarting (or a tunnel that closed an idle socket) produced an error
|
|
106
|
+
* page and then a reload loop. When the connection *re*-establishes, the code
|
|
107
|
+
* the page runs is stale and the page reloads — with its state.
|
|
108
|
+
*
|
|
109
|
+
* @returns {void}
|
|
28
110
|
*/
|
|
29
111
|
export const initHMR = () => {
|
|
112
|
+
const restored = restoreRam();
|
|
113
|
+
if (restored) console.log(`♻️ HMR restored ${restored} state key(s)`);
|
|
114
|
+
connect();
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @returns {void}
|
|
119
|
+
*/
|
|
120
|
+
const connect = () => {
|
|
30
121
|
const protocol = location.protocol === "https:" ? "wss:" : "ws:";
|
|
31
122
|
ws = new WebSocket(`${protocol}//${location.host}`);
|
|
32
123
|
|
|
33
124
|
ws.onopen = () => {
|
|
125
|
+
retries = 0;
|
|
126
|
+
if (everOpened) {
|
|
127
|
+
console.log("🔥 HMR reconnected — the page is stale, reloading");
|
|
128
|
+
reloadKeepingState();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
everOpened = true;
|
|
34
132
|
console.log("🔥 HMR connected");
|
|
35
133
|
};
|
|
36
134
|
|
|
37
135
|
ws.onmessage = (event) => {
|
|
38
|
-
|
|
136
|
+
let data;
|
|
137
|
+
try {
|
|
138
|
+
data = JSON.parse(event.data);
|
|
139
|
+
} catch (e) {
|
|
140
|
+
return; // a proxy or tunnel injecting a non-JSON frame must not kill the client
|
|
141
|
+
}
|
|
39
142
|
|
|
40
143
|
if (data.type === "request-modules") {
|
|
41
144
|
// Send the list of registered module URLs back to the server.
|
|
@@ -50,14 +153,19 @@ export const initHMR = () => {
|
|
|
50
153
|
}
|
|
51
154
|
|
|
52
155
|
if (data.type === "reload") {
|
|
53
|
-
console.log("🔄
|
|
54
|
-
|
|
156
|
+
console.log("🔄 Reload triggered by the server");
|
|
157
|
+
reloadKeepingState();
|
|
55
158
|
}
|
|
56
159
|
};
|
|
57
160
|
|
|
58
161
|
ws.onclose = () => {
|
|
59
|
-
|
|
60
|
-
|
|
162
|
+
if (retries >= MAX_RETRIES) {
|
|
163
|
+
console.warn("🔌 HMR gave up: the dev server is unreachable. Refresh to retry.");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
retries++;
|
|
167
|
+
clearTimeout(retryTimer);
|
|
168
|
+
retryTimer = setTimeout(connect, 1000);
|
|
61
169
|
};
|
|
62
170
|
};
|
|
63
171
|
|
|
@@ -68,16 +176,13 @@ export const initHMR = () => {
|
|
|
68
176
|
*
|
|
69
177
|
* - `css` — hot-reloads matching `<link>` stylesheets by appending a cache-
|
|
70
178
|
* busting timestamp to their `href`, avoiding a full page reload.
|
|
71
|
-
* -
|
|
72
|
-
*
|
|
73
|
-
* - anything else — re-imports the changed module via a dynamic `import()` and
|
|
74
|
-
* dispatches an `hmr:update` CustomEvent so components can react. Falls back
|
|
75
|
-
* to a full reload if the import fails.
|
|
179
|
+
* - anything else — the module graph cannot be rebound in place, so the page
|
|
180
|
+
* reloads and the `useState` RAM is restored from the snapshot.
|
|
76
181
|
*
|
|
77
182
|
* @param {{ file: string, changeType: string }} data - The HMR payload from the server.
|
|
78
183
|
* @param {string} data.file - Relative path of the changed file.
|
|
79
|
-
* @param {string} data.changeType - The kind of change (`"css"`,
|
|
80
|
-
*
|
|
184
|
+
* @param {string} data.changeType - The kind of change (`"css"`, or any other
|
|
185
|
+
* string for a reload with the state kept).
|
|
81
186
|
*/
|
|
82
187
|
const handleHMR = (data) => {
|
|
83
188
|
const { file, changeType } = data;
|
|
@@ -97,26 +202,16 @@ const handleHMR = (data) => {
|
|
|
97
202
|
return;
|
|
98
203
|
}
|
|
99
204
|
|
|
100
|
-
if (changeType === "exports-changed") {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
.then((module) => {
|
|
111
|
-
// Notify any listening components that the module was updated.
|
|
112
|
-
window.dispatchEvent(
|
|
113
|
-
new CustomEvent("hmr:update", { detail: { file, module } }),
|
|
114
|
-
);
|
|
115
|
-
})
|
|
116
|
-
.catch((err) => {
|
|
117
|
-
console.error("HMR import failed:", err);
|
|
118
|
-
window.location.reload();
|
|
119
|
-
});
|
|
205
|
+
if (changeType === "exports-changed") console.log(`📦 Exports changed in ${file}`);
|
|
206
|
+
// A JS change reloads the page and puts the state back.
|
|
207
|
+
//
|
|
208
|
+
// Re-importing the file is not a swap: a widget that already closed over the
|
|
209
|
+
// old function keeps the old function, and the fresh module instance nobody
|
|
210
|
+
// references changes nothing on screen. What the developer should see is the
|
|
211
|
+
// new code, so the module graph is re-fetched — and because `useState` RAM
|
|
212
|
+
// survives the reload, that is not paid for with the form you were filling.
|
|
213
|
+
console.log(`🔥 Reload with state: ${file}`);
|
|
214
|
+
reloadKeepingState();
|
|
120
215
|
};
|
|
121
216
|
|
|
122
217
|
/**
|
package/src/index.d.ts
CHANGED
|
@@ -341,6 +341,8 @@ declare module 'flet-box' {
|
|
|
341
341
|
height?: Size;
|
|
342
342
|
fit?: 'cover' | 'contain' | 'fill' | 'none' | 'scale-down';
|
|
343
343
|
circular?: boolean;
|
|
344
|
+
downloadable?: boolean;
|
|
345
|
+
downloadFileName?: string;
|
|
344
346
|
onLoad?: (widget: Widget) => void;
|
|
345
347
|
onError?: (widget: Widget) => void;
|
|
346
348
|
onPress?: (widget: Widget) => void;
|
|
@@ -355,6 +357,8 @@ declare module 'flet-box' {
|
|
|
355
357
|
bgColor?: Color;
|
|
356
358
|
textColor?: Color;
|
|
357
359
|
shape?: 'circle' | 'rounded' | 'square';
|
|
360
|
+
online?: boolean;
|
|
361
|
+
badgeColor?: Color;
|
|
358
362
|
onPress?: (widget: Widget) => void;
|
|
359
363
|
}
|
|
360
364
|
export function Avatar(props: AvatarProps): Widget & {
|
|
@@ -1363,6 +1367,8 @@ declare module 'flet-box' {
|
|
|
1363
1367
|
child: Widget;
|
|
1364
1368
|
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
1365
1369
|
delay?: number;
|
|
1370
|
+
/** @deprecated Alias of `delay`. */
|
|
1371
|
+
tooltipDelay?: number;
|
|
1366
1372
|
bgColor?: Color;
|
|
1367
1373
|
textColor?: Color;
|
|
1368
1374
|
fontSize?: number;
|
|
@@ -1700,6 +1706,132 @@ declare module 'flet-box' {
|
|
|
1700
1706
|
getSource(): string;
|
|
1701
1707
|
};
|
|
1702
1708
|
|
|
1709
|
+
// =========================================================================
|
|
1710
|
+
// Maps
|
|
1711
|
+
// =========================================================================
|
|
1712
|
+
|
|
1713
|
+
/** A point on the map: `[lat, lng]`, `{lat, lng}` or `{latitude, longitude}`. */
|
|
1714
|
+
export type LatLng = [number, number] | { lat: number; lng: number } | { latitude: number; longitude: number };
|
|
1715
|
+
|
|
1716
|
+
/** `[[south, west], [north, east]]`, a named box, or the points to enclose. */
|
|
1717
|
+
export type MapBounds = [[number, number], [number, number]] | { south: number; west: number; north: number; east: number } | LatLng[];
|
|
1718
|
+
|
|
1719
|
+
/** A pin. A bare `LatLng` is also accepted wherever a marker is expected. */
|
|
1720
|
+
export interface MapMarker {
|
|
1721
|
+
position: LatLng;
|
|
1722
|
+
label?: string;
|
|
1723
|
+
color?: Color;
|
|
1724
|
+
/** Keeps the pooled pin node across updates; auto-generated when omitted. */
|
|
1725
|
+
id?: string | number;
|
|
1726
|
+
/** Degrees clockwise from north, the bearing a GPS fix reports. Turns the pin around its own tip. */
|
|
1727
|
+
rotation?: number;
|
|
1728
|
+
/** `false` lets a tap fall through to the map: a tracked vehicle must not block picking a point. */
|
|
1729
|
+
clickable?: boolean;
|
|
1730
|
+
data?: any;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
/** Payload of `onMapClick`: the geographic point under the cursor. */
|
|
1734
|
+
export interface MapClick {
|
|
1735
|
+
lat: number;
|
|
1736
|
+
lng: number;
|
|
1737
|
+
zoom: number;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
/** Payload of `onMoveEnd`: the view once it stops changing. */
|
|
1741
|
+
export interface MapState {
|
|
1742
|
+
center: { lat: number; lng: number };
|
|
1743
|
+
zoom: number;
|
|
1744
|
+
bounds: { south: number; west: number; north: number; east: number };
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/**
|
|
1748
|
+
* A stroked path — a route, a track, a boundary. Needs at least two usable
|
|
1749
|
+
* points. A bare `LatLng[]` is also accepted wherever a polyline is expected.
|
|
1750
|
+
*/
|
|
1751
|
+
export interface MapPolyline {
|
|
1752
|
+
points: LatLng[];
|
|
1753
|
+
/** Stroke colour. Defaults to `polylineColor`. */
|
|
1754
|
+
color?: Color;
|
|
1755
|
+
/** Stroke width in CSS pixels. Defaults to `4`. */
|
|
1756
|
+
width?: number;
|
|
1757
|
+
opacity?: number;
|
|
1758
|
+
dashed?: boolean;
|
|
1759
|
+
/** Identifies the line to `removePolyline()`; auto-generated when omitted. */
|
|
1760
|
+
id?: string | number;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/** Payload of `onUserUpdate`: one fix from the watcher, in degrees and metres. */
|
|
1764
|
+
export interface MapFix {
|
|
1765
|
+
lat: number;
|
|
1766
|
+
lng: number;
|
|
1767
|
+
/** Radius of 68% confidence in metres, or null when the browser hides it. */
|
|
1768
|
+
accuracy: number | null;
|
|
1769
|
+
/** Degrees clockwise from true north, or null when the device reports none. */
|
|
1770
|
+
heading: number | null;
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
export interface MapsProps extends CommonProps {
|
|
1774
|
+
/** Initial view centre. Defaults to `[20, 0]`. */
|
|
1775
|
+
center?: LatLng;
|
|
1776
|
+
/** Integer zoom level, 0 = the whole world. Defaults to `2`. */
|
|
1777
|
+
zoom?: number;
|
|
1778
|
+
minZoom?: number;
|
|
1779
|
+
maxZoom?: number;
|
|
1780
|
+
/** Positions and/or `{position, label, color, id, data}` records. */
|
|
1781
|
+
markers?: Array<MapMarker | LatLng>;
|
|
1782
|
+
/** Colour of the pins that do not set their own. Defaults to `colors.primary`. */
|
|
1783
|
+
markerColor?: Color;
|
|
1784
|
+
/** Position lists and/or `{points, color, width, opacity, dashed, id}` records. */
|
|
1785
|
+
polylines?: Array<MapPolyline | LatLng[]>;
|
|
1786
|
+
/** Stroke of the lines that do not set their own. Defaults to `colors.primary`. */
|
|
1787
|
+
polylineColor?: Color;
|
|
1788
|
+
/** `{z}/{x}/{y}` tile template. Defaults to OpenStreetMap. */
|
|
1789
|
+
tileUrl?: string;
|
|
1790
|
+
/** Credit line; most providers require one. */
|
|
1791
|
+
attribution?: string;
|
|
1792
|
+
showAttribution?: boolean;
|
|
1793
|
+
showZoomControl?: boolean;
|
|
1794
|
+
/** When false the view only moves through the imperative API. */
|
|
1795
|
+
interactive?: boolean;
|
|
1796
|
+
scrollZoom?: boolean;
|
|
1797
|
+
doubleClickZoom?: boolean;
|
|
1798
|
+
/** Draws the browser position; asks for permission on the first map paint. */
|
|
1799
|
+
showUserLocation?: boolean;
|
|
1800
|
+
/**
|
|
1801
|
+
* Keeps the view centred on the browser position. Implies the dot and asks
|
|
1802
|
+
* the browser for fresh high-accuracy fixes instead of a cached one.
|
|
1803
|
+
*/
|
|
1804
|
+
followUser?: boolean;
|
|
1805
|
+
onMapClick?: (point: MapClick, event: PointerEvent) => void;
|
|
1806
|
+
onMarkerClick?: (marker: MapMarker, event: MouseEvent) => void;
|
|
1807
|
+
onMoveEnd?: (state: MapState) => void;
|
|
1808
|
+
/** Fires on each watched fix, including the ones while `followUser` is off. */
|
|
1809
|
+
onUserUpdate?: (fix: MapFix) => void;
|
|
1810
|
+
}
|
|
1811
|
+
export function Maps(props: MapsProps): Widget & {
|
|
1812
|
+
setCenter(position: LatLng, zoom?: number): Widget;
|
|
1813
|
+
getCenter(): { lat: number; lng: number };
|
|
1814
|
+
getZoom(): number;
|
|
1815
|
+
zoomTo(zoom: number): Widget;
|
|
1816
|
+
panBy(dx: number, dy: number): Widget;
|
|
1817
|
+
fitBounds(bounds: MapBounds, padding?: number): Widget;
|
|
1818
|
+
getState(): MapState;
|
|
1819
|
+
addMarker(marker: MapMarker | LatLng): MapMarker | null;
|
|
1820
|
+
/** Replace the marker with the same id, or append it when that id is new. */
|
|
1821
|
+
setMarker(marker: MapMarker | LatLng): MapMarker | null;
|
|
1822
|
+
removeMarker(idOrMarker: string | number | MapMarker): Widget;
|
|
1823
|
+
clearMarkers(): Widget;
|
|
1824
|
+
getMarkers(): MapMarker[];
|
|
1825
|
+
addPolyline(line: MapPolyline | LatLng[]): MapPolyline | null;
|
|
1826
|
+
removePolyline(idOrLine: string | number | MapPolyline): Widget;
|
|
1827
|
+
clearPolylines(): Widget;
|
|
1828
|
+
getPolylines(): MapPolyline[];
|
|
1829
|
+
setFollowUser(on: boolean): Widget;
|
|
1830
|
+
locate(settings?: { setCenter?: boolean; zoom?: number; timeout?: number; maximumAge?: number; highAccuracy?: boolean }): Promise<{ lat: number; lng: number }> | null;
|
|
1831
|
+
setUserPosition(position: LatLng | number[] | null, settings?: { setCenter?: boolean; zoom?: number }): Widget;
|
|
1832
|
+
render(): Widget;
|
|
1833
|
+
};
|
|
1834
|
+
|
|
1703
1835
|
// =========================================================================
|
|
1704
1836
|
// Core
|
|
1705
1837
|
// =========================================================================
|
package/src/index.js
CHANGED
|
@@ -189,15 +189,20 @@ export const AppBar = (props) => {
|
|
|
189
189
|
|
|
190
190
|
// ========== LEFT SECTION ==========
|
|
191
191
|
// Fixed minimum width ensures the center title stays visually centered
|
|
192
|
-
// even when no leading widget is present.
|
|
192
|
+
// even when no leading widget is present. That balance is only meaningful
|
|
193
|
+
// for a centered title: with `centerTitle: false` the title is flush-left,
|
|
194
|
+
// so reserving the empty 48px zone would push it past the page's content
|
|
195
|
+
// edge. Same reasoning for the title zone's 8px gutter.
|
|
196
|
+
const hasLeading = finalLeading instanceof HTMLElement;
|
|
197
|
+
const balanceZones = centerTitle || hasLeading;
|
|
193
198
|
const leftSection = Container({
|
|
194
199
|
display: "flex",
|
|
195
200
|
alignItems: "center",
|
|
196
201
|
bgColor: "transparent",
|
|
197
202
|
justifyContent: "flex-start",
|
|
198
|
-
minWidth: 48,
|
|
203
|
+
minWidth: balanceZones ? 48 : 0,
|
|
199
204
|
flexShrink: 0,
|
|
200
|
-
child:
|
|
205
|
+
child: hasLeading ? finalLeading : null,
|
|
201
206
|
});
|
|
202
207
|
appBar.appendChild(leftSection);
|
|
203
208
|
|
|
@@ -224,9 +229,15 @@ export const AppBar = (props) => {
|
|
|
224
229
|
// pushes the actions past the viewport edge on narrow screens.
|
|
225
230
|
flex: 1,
|
|
226
231
|
display: "flex",
|
|
232
|
+
// Container defaults to `flex-direction: column`, which silently rewrites
|
|
233
|
+
// the two axes: `alignItems: center` becomes horizontal centring and
|
|
234
|
+
// `justifyContent` (the flag that carries `centerTitle`) becomes vertical.
|
|
235
|
+
// A string title hid this because its Text fills the width; an
|
|
236
|
+
// auto-width widget title got centred no matter what `centerTitle` said.
|
|
237
|
+
flexDirection: "row",
|
|
227
238
|
alignItems: "center",
|
|
228
239
|
justifyContent: centerTitle ? "center" : "flex-start",
|
|
229
|
-
padding: "0 8px",
|
|
240
|
+
padding: balanceZones ? "0 8px" : "0",
|
|
230
241
|
bgColor: "transparent",
|
|
231
242
|
overflow: "hidden",
|
|
232
243
|
minWidth: 0,
|
|
@@ -173,7 +173,15 @@ export const Drawer = (props = {}) => {
|
|
|
173
173
|
paddingBottom: 8,
|
|
174
174
|
overflow: "auto",
|
|
175
175
|
backgroundColor: bgColor,
|
|
176
|
-
|
|
176
|
+
// This body is a scroll container inside a fixed-height flex column,
|
|
177
|
+
// and every child defaults to `flex-shrink: 1`. Left alone, a short
|
|
178
|
+
// drawer squeezed its rows down to their own padding (a DrawerItem
|
|
179
|
+
// collapsed to ~24px) instead of overflowing. Pinning the children
|
|
180
|
+
// keeps them at content height so the body scrolls as intended.
|
|
181
|
+
children: body.map((child) => {
|
|
182
|
+
if (child && child.style) child.style.flexShrink = "0";
|
|
183
|
+
return child;
|
|
184
|
+
}),
|
|
177
185
|
}),
|
|
178
186
|
|
|
179
187
|
footer &&
|
|
@@ -480,6 +480,7 @@ export const ALL_PROPS = [
|
|
|
480
480
|
{ prop: "widthExpanded", domProp: "widthExpanded", type: "special" },
|
|
481
481
|
{ prop: "widthCollapsed", domProp: "widthCollapsed", type: "special" },
|
|
482
482
|
{ prop: "showTooltip", domProp: "showTooltip", type: "special" },
|
|
483
|
+
// Legacy alias of `delay`, still honoured by Tooltip.
|
|
483
484
|
{ prop: "tooltipDelay", domProp: "tooltipDelay", type: "special" },
|
|
484
485
|
|
|
485
486
|
// ---- ListView / GridView ----
|
|
@@ -702,6 +703,7 @@ export const ALL_PROPS = [
|
|
|
702
703
|
// ---- Avatar / Badge / Icon / Chip ----
|
|
703
704
|
{ prop: "icon", domProp: "icon", type: "special" },
|
|
704
705
|
{ prop: "shape", domProp: "shape", type: "special" },
|
|
706
|
+
// Presence dot: `online` shows it on Avatar, `badgeColor` paints it.
|
|
705
707
|
{ prop: "online", domProp: "online", type: "special" },
|
|
706
708
|
{ prop: "badge", domProp: "badge", type: "special" },
|
|
707
709
|
{ prop: "badgeColor", domProp: "badgeColor", type: "special" },
|
|
@@ -732,9 +734,12 @@ export const ALL_PROPS = [
|
|
|
732
734
|
{ prop: "headingColor", domProp: "headingColor", type: "special" },
|
|
733
735
|
{ prop: "allowDangerousHtml", domProp: "allowDangerousHtml", type: "special" },
|
|
734
736
|
|
|
735
|
-
// ----
|
|
737
|
+
// ---- Image ----
|
|
738
|
+
// `downloadable` turns a click into a save, `downloadFileName` names the file.
|
|
736
739
|
{ prop: "downloadable", domProp: "downloadable", type: "special" },
|
|
737
740
|
{ prop: "downloadFileName", domProp: "downloadFileName", type: "special" },
|
|
741
|
+
|
|
742
|
+
// ---- InstallButton ----
|
|
738
743
|
{ prop: "onInstalled", domProp: "onInstalled", type: "special" },
|
|
739
744
|
|
|
740
745
|
// ---- MatrixRain ----
|
|
@@ -748,6 +753,32 @@ export const ALL_PROPS = [
|
|
|
748
753
|
{ prop: "fgColor", domProp: "fgColor", type: "special" },
|
|
749
754
|
{ prop: "errorCorrection", domProp: "errorCorrection", type: "special" },
|
|
750
755
|
|
|
756
|
+
// ---- Maps ----
|
|
757
|
+
// `center` and `markers` are already registered above; the rest are Maps-only.
|
|
758
|
+
// All are inert here: Maps destructures them before the factory sees them and
|
|
759
|
+
// re-applies them through `composeUpdate`, so the registry entry only has to
|
|
760
|
+
// keep them out of the unknown-prop fallback (an invalid inline style for a
|
|
761
|
+
// `{lat, lng}` object, a bogus `mapclick` listener for `onMapClick`).
|
|
762
|
+
{ prop: "zoom", domProp: "zoom", type: "special" },
|
|
763
|
+
{ prop: "minZoom", domProp: "minZoom", type: "special" },
|
|
764
|
+
{ prop: "maxZoom", domProp: "maxZoom", type: "special" },
|
|
765
|
+
{ prop: "markerColor", domProp: "markerColor", type: "special" },
|
|
766
|
+
{ prop: "polylines", domProp: "polylines", type: "special" },
|
|
767
|
+
{ prop: "polylineColor", domProp: "polylineColor", type: "special" },
|
|
768
|
+
{ prop: "tileUrl", domProp: "tileUrl", type: "special" },
|
|
769
|
+
{ prop: "attribution", domProp: "attribution", type: "special" },
|
|
770
|
+
{ prop: "showAttribution", domProp: "showAttribution", type: "special" },
|
|
771
|
+
{ prop: "showZoomControl", domProp: "showZoomControl", type: "special" },
|
|
772
|
+
{ prop: "interactive", domProp: "interactive", type: "special" },
|
|
773
|
+
{ prop: "scrollZoom", domProp: "scrollZoom", type: "special" },
|
|
774
|
+
{ prop: "doubleClickZoom", domProp: "doubleClickZoom", type: "special" },
|
|
775
|
+
{ prop: "showUserLocation", domProp: "showUserLocation", type: "special" },
|
|
776
|
+
{ prop: "followUser", domProp: "followUser", type: "special" },
|
|
777
|
+
{ prop: "onMapClick", domProp: "onMapClick", type: "special" },
|
|
778
|
+
{ prop: "onMarkerClick", domProp: "onMarkerClick", type: "special" },
|
|
779
|
+
{ prop: "onMoveEnd", domProp: "onMoveEnd", type: "special" },
|
|
780
|
+
{ prop: "onUserUpdate", domProp: "onUserUpdate", type: "special" },
|
|
781
|
+
|
|
751
782
|
// ---- Image fit ----
|
|
752
783
|
{ prop: "fit", domProp: "objectFit", type: "style", unit: "none", reactive: true },
|
|
753
784
|
{ prop: "objectFit", domProp: "objectFit", type: "style", unit: "none", reactive: true },
|