@protobase/ui 0.13.0 → 0.13.4
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/dist/app/create/create-page.js +1 -0
- package/dist/app/current-address.d.ts +3 -2
- package/dist/app/current-address.js +2 -1
- package/dist/app/delete/use-delete-flow.js +2 -1
- package/dist/app/displays/card-carousel.js +4 -1
- package/dist/app/displays/card-photos.js +6 -3
- package/dist/app/displays/map-display.js +83 -23
- package/dist/app/displays/map-groups.js +1 -8
- package/dist/app/displays/record-card.js +8 -6
- package/dist/app/displays/use-map-records.js +44 -0
- package/dist/app/embedded-list/embedded-list.js +3 -1
- package/dist/app/file-field.js +16 -13
- package/dist/app/list-page.js +6 -2
- package/dist/app/map/map-area.js +53 -0
- package/dist/app/map/records-map.js +91 -38
- package/dist/app/pages/field-value.js +22 -9
- package/dist/app/use-record-editor.js +2 -1
- package/dist/data/api-provider.d.ts +2 -0
- package/dist/data/query-keys.js +9 -0
- package/dist/data/use-update-record.js +1 -0
- package/dist/data-grid/cells.js +8 -5
- package/dist/live/file-image.js +35 -0
- package/dist/live/file-values.js +17 -1
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-BBslM0jG.js +66 -0
- package/dist/shell/assets/index-CYWfU_c1.css +2 -0
- package/dist/shell/assets/{index-D7IcSr6n.js → index-rR2iasG9.js} +1 -1
- package/dist/shell/assets/{maplibre-gl-Bt-3iOM-.js → maplibre-gl-estTAzEH.js} +1 -1
- package/dist/shell/assets/{record-map-DL4b9dZO.js → record-map-B57A7PQ0.js} +2 -2
- package/dist/shell/assets/records-map-BwsWXD8A.js +1 -0
- package/dist/shell/assets/shared-protobase-ui-CE7mIalS.js +1 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/package.json +5 -4
- package/dist/shell/assets/app-DLjaMVJD.js +0 -66
- package/dist/shell/assets/index-BYPphaEm.css +0 -2
- package/dist/shell/assets/records-map-D0EOO5kZ.js +0 -1
- package/dist/shell/assets/shared-protobase-ui-B8UXOIeW.js +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { cn } from "../../lib/cn.js";
|
|
2
|
-
import { groupPoints
|
|
2
|
+
import { groupPoints } from "../displays/map-groups.js";
|
|
3
3
|
import { useEffect, useRef, useState } from "react";
|
|
4
4
|
import { MapPin } from "lucide-react";
|
|
5
5
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -12,18 +12,29 @@ const fitOptions = {
|
|
|
12
12
|
maxZoom: 15,
|
|
13
13
|
duration: 0
|
|
14
14
|
};
|
|
15
|
+
const bucketOptions = {
|
|
16
|
+
padding: 48,
|
|
17
|
+
maxZoom: 16,
|
|
18
|
+
duration: 300
|
|
19
|
+
};
|
|
15
20
|
const markerHeight = 32;
|
|
21
|
+
const compact = new Intl.NumberFormat("en-IE", {
|
|
22
|
+
notation: "compact",
|
|
23
|
+
maximumFractionDigits: 1
|
|
24
|
+
});
|
|
25
|
+
const corners = (bounds) => [[bounds.west, bounds.south], [bounds.east, bounds.north]];
|
|
16
26
|
/**
|
|
17
27
|
* A map of `style` with a marker for each group of records that lie close together at the zoom, showing how many it
|
|
18
|
-
* stands for
|
|
19
|
-
* clears the selection. A chosen marker the covered bottom would hide
|
|
28
|
+
* stands for, and a circle with the count of each bucket, which zooms in on its records when chosen. Choosing a marker
|
|
29
|
+
* selects its records; choosing the map elsewhere clears the selection. A chosen marker the covered bottom would hide
|
|
30
|
+
* moves up into the open part of the map.
|
|
20
31
|
*/
|
|
21
|
-
const RecordsMap = ({ style, points, selected, covered, onSelect, onClear, markerLabel }) => {
|
|
32
|
+
const RecordsMap = ({ style, initial, points, buckets, fit, onFitted, onMove, selected, covered, onSelect, onClear, markerLabel, bucketLabel }) => {
|
|
22
33
|
const ref = useRef(null);
|
|
23
34
|
const [groups, setGroups] = useState([]);
|
|
24
35
|
const byKey = new globalThis.Map(points.map((point) => [point.key, point]));
|
|
25
36
|
const pointKeys = points.map((point) => point.key).join(",");
|
|
26
|
-
const
|
|
37
|
+
const fitKey = fit && JSON.stringify(fit);
|
|
27
38
|
const regroup = () => {
|
|
28
39
|
const map = ref.current?.getMap();
|
|
29
40
|
if (!map) return;
|
|
@@ -32,12 +43,34 @@ const RecordsMap = ({ style, points, selected, covered, onSelect, onClear, marke
|
|
|
32
43
|
...map.project([point.longitude, point.latitude])
|
|
33
44
|
})), groupRadius));
|
|
34
45
|
};
|
|
35
|
-
|
|
46
|
+
const report = () => {
|
|
36
47
|
const map = ref.current?.getMap();
|
|
37
48
|
if (!map) return;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
49
|
+
const bounds = map.getBounds();
|
|
50
|
+
const center = map.getCenter();
|
|
51
|
+
onMove({
|
|
52
|
+
bounds: {
|
|
53
|
+
south: bounds.getSouth(),
|
|
54
|
+
west: bounds.getWest(),
|
|
55
|
+
north: bounds.getNorth(),
|
|
56
|
+
east: bounds.getEast()
|
|
57
|
+
},
|
|
58
|
+
zoom: map.getZoom()
|
|
59
|
+
}, {
|
|
60
|
+
latitude: center.lat,
|
|
61
|
+
longitude: center.lng,
|
|
62
|
+
zoom: map.getZoom()
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
const fitNow = () => {
|
|
66
|
+
const map = ref.current?.getMap();
|
|
67
|
+
if (!map || !fit) return;
|
|
68
|
+
map.fitBounds(corners(fit), fitOptions);
|
|
69
|
+
onFitted();
|
|
70
|
+
report();
|
|
71
|
+
};
|
|
72
|
+
useEffect(regroup, [pointKeys]);
|
|
73
|
+
useEffect(fitNow, [fitKey]);
|
|
41
74
|
const chosenAt = groups.find((group) => selected !== void 0 && group.keys.some((key) => selected.includes(key)))?.at;
|
|
42
75
|
useEffect(() => {
|
|
43
76
|
const map = ref.current?.getMap();
|
|
@@ -56,10 +89,7 @@ const RecordsMap = ({ style, points, selected, covered, onSelect, onClear, marke
|
|
|
56
89
|
ref,
|
|
57
90
|
mapLib: maplibre,
|
|
58
91
|
mapStyle: style,
|
|
59
|
-
initialViewState:
|
|
60
|
-
bounds,
|
|
61
|
-
fitBoundsOptions: fitOptions
|
|
62
|
-
} : {
|
|
92
|
+
initialViewState: initial ?? {
|
|
63
93
|
latitude: 0,
|
|
64
94
|
longitude: 0,
|
|
65
95
|
zoom: 1
|
|
@@ -68,38 +98,61 @@ const RecordsMap = ({ style, points, selected, covered, onSelect, onClear, marke
|
|
|
68
98
|
width: "100%",
|
|
69
99
|
height: "100%"
|
|
70
100
|
},
|
|
71
|
-
onLoad:
|
|
101
|
+
onLoad: () => {
|
|
102
|
+
regroup();
|
|
103
|
+
if (fit) fitNow();
|
|
104
|
+
else report();
|
|
105
|
+
},
|
|
72
106
|
onZoomEnd: regroup,
|
|
107
|
+
onMoveEnd: report,
|
|
73
108
|
onClick: onClear,
|
|
74
|
-
children: [
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
anchor: "bottom",
|
|
82
|
-
style: { zIndex: chosen ? 1 : 0 },
|
|
83
|
-
children: /* @__PURE__ */ jsxs("button", {
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ jsx(NavigationControl, { showCompass: false }),
|
|
111
|
+
buckets.map((bucket) => /* @__PURE__ */ jsx(Marker, {
|
|
112
|
+
latitude: bucket.latitude,
|
|
113
|
+
longitude: bucket.longitude,
|
|
114
|
+
anchor: "center",
|
|
115
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
84
116
|
type: "button",
|
|
85
|
-
"aria-label":
|
|
86
|
-
"
|
|
87
|
-
className: "relative block cursor-pointer",
|
|
117
|
+
"aria-label": bucketLabel(bucket.count),
|
|
118
|
+
className: "flex size-10 cursor-pointer items-center justify-center rounded-full border-2 border-background bg-primary text-xs font-semibold text-primary-foreground tabular-nums shadow",
|
|
88
119
|
onClick: (event) => {
|
|
89
120
|
event.stopPropagation();
|
|
90
|
-
|
|
121
|
+
ref.current?.getMap().fitBounds(corners(bucket.bounds), bucketOptions);
|
|
91
122
|
},
|
|
92
|
-
children:
|
|
93
|
-
"aria-hidden": true,
|
|
94
|
-
className: cn("size-8 drop-shadow", chosen ? "fill-foreground text-background" : "fill-primary text-primary-foreground"),
|
|
95
|
-
strokeWidth: 1.5
|
|
96
|
-
}), group.keys.length > 1 && /* @__PURE__ */ jsx("span", {
|
|
97
|
-
className: "absolute -right-2 -top-1 min-w-5 rounded-full border border-background bg-foreground px-1 text-center text-[11px] font-medium leading-4 text-background tabular-nums",
|
|
98
|
-
children: group.keys.length
|
|
99
|
-
})]
|
|
123
|
+
children: compact.format(bucket.count)
|
|
100
124
|
})
|
|
101
|
-
},
|
|
102
|
-
|
|
125
|
+
}, `${bucket.latitude},${bucket.longitude}`)),
|
|
126
|
+
groups.map((group) => {
|
|
127
|
+
const at = byKey.get(group.at);
|
|
128
|
+
if (!at) return null;
|
|
129
|
+
const chosen = selected !== void 0 && group.keys.some((key) => selected.includes(key));
|
|
130
|
+
return /* @__PURE__ */ jsx(Marker, {
|
|
131
|
+
latitude: at.latitude,
|
|
132
|
+
longitude: at.longitude,
|
|
133
|
+
anchor: "bottom",
|
|
134
|
+
style: { zIndex: chosen ? 1 : 0 },
|
|
135
|
+
children: /* @__PURE__ */ jsxs("button", {
|
|
136
|
+
type: "button",
|
|
137
|
+
"aria-label": markerLabel(group.keys),
|
|
138
|
+
"aria-pressed": chosen,
|
|
139
|
+
className: "relative block cursor-pointer",
|
|
140
|
+
onClick: (event) => {
|
|
141
|
+
event.stopPropagation();
|
|
142
|
+
onSelect(group.keys);
|
|
143
|
+
},
|
|
144
|
+
children: [/* @__PURE__ */ jsx(MapPin, {
|
|
145
|
+
"aria-hidden": true,
|
|
146
|
+
className: cn("size-8 drop-shadow", chosen ? "fill-foreground text-background" : "fill-primary text-primary-foreground"),
|
|
147
|
+
strokeWidth: 1.5
|
|
148
|
+
}), group.keys.length > 1 && /* @__PURE__ */ jsx("span", {
|
|
149
|
+
className: "absolute -right-2 -top-1 min-w-5 rounded-full border border-background bg-foreground px-1 text-center text-[11px] font-medium leading-4 text-background tabular-nums",
|
|
150
|
+
children: group.keys.length
|
|
151
|
+
})]
|
|
152
|
+
})
|
|
153
|
+
}, group.at);
|
|
154
|
+
})
|
|
155
|
+
]
|
|
103
156
|
});
|
|
104
157
|
};
|
|
105
158
|
//#endregion
|
|
@@ -2,14 +2,15 @@ import { cn } from "../../lib/cn.js";
|
|
|
2
2
|
import { keys } from "../../data/query-keys.js";
|
|
3
3
|
import { useClient } from "../../data/api-provider.js";
|
|
4
4
|
import { fetchLabels } from "../../live/relation-labels.js";
|
|
5
|
-
import { fileShown } from "../../live/file-values.js";
|
|
5
|
+
import { fileShown, shownImage } from "../../live/file-values.js";
|
|
6
6
|
import { displayValue } from "../../live/display-value.js";
|
|
7
7
|
import { useAdminMeta } from "../meta-gate.js";
|
|
8
8
|
import { AppLink } from "../use-app-address.js";
|
|
9
|
+
import { FileImage } from "../../live/file-image.js";
|
|
9
10
|
import { useShownWidget } from "../field-types/shown-widget.js";
|
|
10
11
|
import { WidgetPart } from "../field-types/widget-part.js";
|
|
11
12
|
import { ValueBadge } from "../value-badge.js";
|
|
12
|
-
import { jsx } from "react/jsx-runtime";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
14
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
14
15
|
//#region src/app/pages/field-value.tsx
|
|
15
16
|
const RelationValue = ({ field, value, known }) => {
|
|
@@ -50,13 +51,25 @@ const BuiltInValue = ({ model, field, value, format, relationLabel }) => {
|
|
|
50
51
|
known: relationLabel
|
|
51
52
|
});
|
|
52
53
|
const file = field.type === "file" ? fileShown(value) : void 0;
|
|
53
|
-
if (file && "url" in file && file.url)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
if (file && "url" in file && file.url) {
|
|
55
|
+
const image = shownImage(value, {
|
|
56
|
+
width: 40,
|
|
57
|
+
height: 40
|
|
58
|
+
});
|
|
59
|
+
return /* @__PURE__ */ jsxs("span", {
|
|
60
|
+
className: "inline-flex min-w-0 items-center gap-2",
|
|
61
|
+
children: [image && /* @__PURE__ */ jsx(FileImage, {
|
|
62
|
+
image,
|
|
63
|
+
className: "size-10 shrink-0 rounded border border-border object-cover"
|
|
64
|
+
}), /* @__PURE__ */ jsx("a", {
|
|
65
|
+
href: file.url,
|
|
66
|
+
target: "_blank",
|
|
67
|
+
rel: "noreferrer",
|
|
68
|
+
className: "truncate font-medium text-primary-text hover:underline",
|
|
69
|
+
children: file.name
|
|
70
|
+
})]
|
|
71
|
+
});
|
|
72
|
+
}
|
|
60
73
|
const text = displayValue(field, value, hints);
|
|
61
74
|
if (field.type === "enum" || hints.format === "badge") return /* @__PURE__ */ jsx(ValueBadge, {
|
|
62
75
|
value,
|
|
@@ -106,7 +106,8 @@ const useRecordEditor = ({ model, stored, recordKey, mode, subject, draftKey, li
|
|
|
106
106
|
await Promise.all([
|
|
107
107
|
queryClient.invalidateQueries({ queryKey: keys.record(model.name, recordKey) }),
|
|
108
108
|
queryClient.invalidateQueries({ queryKey: ["list"] }),
|
|
109
|
-
queryClient.invalidateQueries({ queryKey: ["page"] })
|
|
109
|
+
queryClient.invalidateQueries({ queryKey: ["page"] }),
|
|
110
|
+
queryClient.invalidateQueries({ queryKey: ["map"] })
|
|
110
111
|
]);
|
|
111
112
|
lines?.reset();
|
|
112
113
|
};
|
|
@@ -28,6 +28,7 @@ export declare const useClient: () => {
|
|
|
28
28
|
filter?: import("@protobase/client").FilterInput;
|
|
29
29
|
buckets?: number;
|
|
30
30
|
}) => Promise<import("@protobase/client").Histogram>;
|
|
31
|
+
map: <R extends import("@protobase/client").ResourceRef>(resource: R, params: import("@protobase/client").MapParams) => Promise<import("@protobase/client").MapResult<import("@protobase/client").RecordOf<R>>>;
|
|
31
32
|
seek: <R extends import("@protobase/client").ResourceRef>(resource: R, params: import("@protobase/client").ListParams & {
|
|
32
33
|
position: number;
|
|
33
34
|
}) => Promise<import("@protobase/client").ListPage<import("@protobase/client").RecordOf<R>>>;
|
|
@@ -66,6 +67,7 @@ export declare const useResourceClient: (resource: string) => {
|
|
|
66
67
|
filter?: import("@protobase/client").FilterInput;
|
|
67
68
|
buckets?: number;
|
|
68
69
|
}) => Promise<import("@protobase/client").Histogram>;
|
|
70
|
+
map: <R extends import("@protobase/client").ResourceRef>(resource: R, params: import("@protobase/client").MapParams) => Promise<import("@protobase/client").MapResult<import("@protobase/client").RecordOf<R>>>;
|
|
69
71
|
seek: <R extends import("@protobase/client").ResourceRef>(resource: R, params: import("@protobase/client").ListParams & {
|
|
70
72
|
position: number;
|
|
71
73
|
}) => Promise<import("@protobase/client").ListPage<import("@protobase/client").RecordOf<R>>>;
|
package/dist/data/query-keys.js
CHANGED
|
@@ -66,6 +66,15 @@ const keys = {
|
|
|
66
66
|
buckets,
|
|
67
67
|
filter
|
|
68
68
|
],
|
|
69
|
+
/** The records or buckets a map shows; `filter` holds its bounding box. */
|
|
70
|
+
map: (resource, position, zoom, filter, orderBy) => [
|
|
71
|
+
"map",
|
|
72
|
+
resource,
|
|
73
|
+
position,
|
|
74
|
+
zoom,
|
|
75
|
+
filter,
|
|
76
|
+
orderBy
|
|
77
|
+
],
|
|
69
78
|
relatedLabel: (resource, id) => [
|
|
70
79
|
"related-label",
|
|
71
80
|
resource,
|
|
@@ -18,6 +18,7 @@ const useUpdateRecord = (resource, key) => {
|
|
|
18
18
|
queryClient.setQueryData(keys.record(resource, encoded), stored);
|
|
19
19
|
queryClient.invalidateQueries({ queryKey: ["page", resource] });
|
|
20
20
|
queryClient.invalidateQueries({ queryKey: ["list", resource] });
|
|
21
|
+
queryClient.invalidateQueries({ queryKey: ["map", resource] });
|
|
21
22
|
}
|
|
22
23
|
});
|
|
23
24
|
};
|
package/dist/data-grid/cells.js
CHANGED
|
@@ -4,7 +4,8 @@ import { Badge } from "../primitives/badge.js";
|
|
|
4
4
|
import { formatMoney } from "../format/money.js";
|
|
5
5
|
import { formatInt, formatPercent, formatSigned } from "../format/number.js";
|
|
6
6
|
import { formatDate, formatDateTime } from "../format/date.js";
|
|
7
|
-
import { fileShown,
|
|
7
|
+
import { fileShown, shownImage } from "../live/file-values.js";
|
|
8
|
+
import { FileImage } from "../live/file-image.js";
|
|
8
9
|
import { userById } from "../mocks/users.js";
|
|
9
10
|
import { valueColor } from "../live/value-color.js";
|
|
10
11
|
import { Check, Paperclip } from "lucide-react";
|
|
@@ -75,12 +76,14 @@ const renderCell = (spec, value) => {
|
|
|
75
76
|
className: "truncate text-danger-text",
|
|
76
77
|
children: file.name
|
|
77
78
|
});
|
|
78
|
-
const
|
|
79
|
+
const image = shownImage(value, {
|
|
80
|
+
width: 20,
|
|
81
|
+
height: 20
|
|
82
|
+
});
|
|
79
83
|
return /* @__PURE__ */ jsxs("span", {
|
|
80
84
|
className: "inline-flex min-w-0 items-center gap-2",
|
|
81
|
-
children: [
|
|
82
|
-
|
|
83
|
-
alt: "",
|
|
85
|
+
children: [image ? /* @__PURE__ */ jsx(FileImage, {
|
|
86
|
+
image,
|
|
84
87
|
className: "size-5 shrink-0 rounded-sm object-cover"
|
|
85
88
|
}) : /* @__PURE__ */ jsx(Paperclip, { className: "size-3.5 shrink-0 text-muted-foreground" }), /* @__PURE__ */ jsx("span", {
|
|
86
89
|
className: "truncate",
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { thumbHashToDataURL } from "thumbhash";
|
|
4
|
+
//#region src/live/file-image.tsx
|
|
5
|
+
const placeholderImage = (hash) => {
|
|
6
|
+
let binary;
|
|
7
|
+
try {
|
|
8
|
+
binary = atob(hash);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
if (error instanceof DOMException && error.name === "InvalidCharacterError") return void 0;
|
|
11
|
+
throw error;
|
|
12
|
+
}
|
|
13
|
+
return thumbHashToDataURL(Uint8Array.from(binary, (char) => char.charCodeAt(0)));
|
|
14
|
+
};
|
|
15
|
+
/** An image of a file, its placeholder drawn blurred behind it until it has loaded. */
|
|
16
|
+
const FileImage = ({ image, className, loading }) => {
|
|
17
|
+
const [loaded, setLoaded] = useState();
|
|
18
|
+
const placeholder = useMemo(() => image.placeholder === void 0 ? void 0 : placeholderImage(image.placeholder), [image.placeholder]);
|
|
19
|
+
const waiting = placeholder !== void 0 && loaded !== image.url;
|
|
20
|
+
return /* @__PURE__ */ jsx("img", {
|
|
21
|
+
src: image.url,
|
|
22
|
+
alt: "",
|
|
23
|
+
loading,
|
|
24
|
+
decoding: "async",
|
|
25
|
+
onLoad: () => setLoaded(image.url),
|
|
26
|
+
className,
|
|
27
|
+
style: waiting ? {
|
|
28
|
+
backgroundImage: `url("${placeholder}")`,
|
|
29
|
+
backgroundSize: "cover",
|
|
30
|
+
backgroundPosition: "center"
|
|
31
|
+
} : void 0
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
export { FileImage };
|
package/dist/live/file-values.js
CHANGED
|
@@ -34,6 +34,22 @@ const formatSize = (bytes) => {
|
|
|
34
34
|
};
|
|
35
35
|
/** Raster images the browser shows; SVG is never shown, since it can carry scripts. */
|
|
36
36
|
const isPreviewable = (type) => type.startsWith("image/") && type !== "image/svg+xml";
|
|
37
|
+
const density = 2;
|
|
38
|
+
const unbounded = 1e5;
|
|
39
|
+
const covers = (variant, box) => (variant.width !== void 0 || variant.height !== void 0) && (variant.width ?? unbounded) >= box.width * density && (variant.height ?? unbounded) >= box.height * density;
|
|
40
|
+
const area = (variant) => (variant.width ?? unbounded) * (variant.height ?? unbounded);
|
|
41
|
+
/**
|
|
42
|
+
* The picture of a file for `box`: the link of its smallest variant large enough for it, else the file's own, with its
|
|
43
|
+
* placeholder; `undefined` for a file that is no raster image with a link.
|
|
44
|
+
*/
|
|
45
|
+
const shownImage = (value, box) => {
|
|
46
|
+
const file = fileShown(value);
|
|
47
|
+
if (!file || "missing" in file || !file.url || !isPreviewable(file.type)) return void 0;
|
|
48
|
+
return {
|
|
49
|
+
url: ("variants" in file && file.variants ? Object.values(file.variants) : []).filter((variant) => covers(variant, box)).sort((a, b) => area(a) - area(b))[0]?.url ?? file.url,
|
|
50
|
+
..."placeholder" in file && file.placeholder !== void 0 && { placeholder: file.placeholder }
|
|
51
|
+
};
|
|
52
|
+
};
|
|
37
53
|
/** The file input's `accept`: the field's allowed types, or nothing when it takes any. */
|
|
38
54
|
const acceptOf = (field) => {
|
|
39
55
|
const accept = field.file?.accept ?? [];
|
|
@@ -52,4 +68,4 @@ const fileDiffers = (draft, stored) => {
|
|
|
52
68
|
return uri(draft) !== uri(stored);
|
|
53
69
|
};
|
|
54
70
|
//#endregion
|
|
55
|
-
export { acceptOf, fileDiffers, filePatchValue, fileShown, formatSize, isFileDraft, isPreviewable, isStoredFile };
|
|
71
|
+
export { acceptOf, fileDiffers, filePatchValue, fileShown, formatSize, isFileDraft, isPreviewable, isStoredFile, shownImage };
|
package/dist/package.js
CHANGED