@protobase/ui 0.46.1 → 0.46.3
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/displays/card-carousel.js +16 -72
- package/dist/app/displays/card-photos.js +1 -5
- package/dist/app/displays/messages.js +2 -8
- package/dist/carousel/carousel-controls.js +54 -0
- package/dist/carousel/carousel-images.d.ts +10 -0
- package/dist/carousel/carousel-images.js +20 -0
- package/dist/carousel/carousel-steps.js +7 -0
- package/dist/carousel/carousel-track.js +18 -0
- package/dist/carousel/carousel.d.ts +20 -0
- package/dist/carousel/carousel.js +41 -0
- package/dist/carousel/index.d.ts +3 -0
- package/dist/carousel/messages.js +16 -0
- package/dist/carousel/use-carousel.js +36 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -1
- package/dist/live/file-image.js +2 -2
- package/dist/package.js +1 -1
- package/dist/shell/assets/app-BDvKMNNB.js +9 -0
- package/dist/shell/assets/index-CY22munM.js +2 -0
- package/dist/shell/assets/index-DHaeB8cs.css +2 -0
- package/dist/shell/assets/{records-map-B_aiZysV.js → records-map-DybHhZlM.js} +2 -2
- package/dist/shell/assets/shared-protobase-ui-TOG-RVkz.js +2 -0
- package/dist/shell/index.html +4 -4
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/shell/protobase.sw.js +2 -2
- package/dist/shell-source-maps/assets/app-BDvKMNNB.js.map +1 -0
- package/dist/shell-source-maps/assets/{index-CEikeSpI.js.map → index-CY22munM.js.map} +1 -1
- package/dist/shell-source-maps/assets/{records-map-B_aiZysV.js.map → records-map-DybHhZlM.js.map} +1 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-TOG-RVkz.js.map +1 -0
- package/package.json +4 -4
- package/dist/shell/assets/app-iRrG9UoZ.js +0 -9
- package/dist/shell/assets/index-BZXw3deq.css +0 -2
- package/dist/shell/assets/index-CEikeSpI.js +0 -2
- package/dist/shell/assets/shared-protobase-ui-C8zWDzwA.js +0 -2
- package/dist/shell-source-maps/assets/app-iRrG9UoZ.js.map +0 -1
- package/dist/shell-source-maps/assets/shared-protobase-ui-C8zWDzwA.js.map +0 -1
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { CarouselControls } from "../../carousel/carousel-controls.js";
|
|
2
|
+
import { CarouselTrack } from "../../carousel/carousel-track.js";
|
|
3
|
+
import { useCarousel } from "../../carousel/use-carousel.js";
|
|
3
4
|
import { CardImageFrame, CardPhoto } from "./card-image.js";
|
|
4
|
-
import { cardPhotoList
|
|
5
|
-
import {
|
|
6
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
7
|
-
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
import { cardPhotoList } from "./card-photos.js";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
7
|
//#region src/app/displays/card-carousel.tsx
|
|
9
|
-
const arrow = "pointer-events-auto absolute top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-full bg-background/85 text-foreground shadow-sm opacity-0 transition-opacity group-has-[[data-card-photos]:hover]/card:opacity-100 group-hover/arrows:opacity-100 group-has-[:focus-visible]/arrows:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30";
|
|
10
|
-
const touchArrow = "[@media(hover:none)]:opacity-100";
|
|
11
8
|
/**
|
|
12
9
|
* A card whose picture steps through its photos, the card's own image first: a track that snaps photo by photo, swiped
|
|
13
10
|
* on a touch screen or moved by arrows, with dots marking the one in view. The photos come with the card's row, in the
|
|
@@ -16,73 +13,20 @@ const touchArrow = "[@media(hover:none)]:opacity-100";
|
|
|
16
13
|
* does not open the record either. The card's list item must be `relative group/card`.
|
|
17
14
|
*/
|
|
18
15
|
const CardCarousel = ({ photos, cover, swipe = true, children }) => {
|
|
19
|
-
const track = useRef(null);
|
|
20
|
-
const aim = useRef(void 0);
|
|
21
|
-
const [index, setIndex] = useState(0);
|
|
22
16
|
const list = cardPhotoList(cover, photos);
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
if (scroller) scroller.scrollTo({ left: position * scroller.clientWidth });
|
|
26
|
-
};
|
|
27
|
-
const press = (by) => {
|
|
28
|
-
aim.current = shownPhoto((aim.current ?? index) + by, list.length);
|
|
29
|
-
show(aim.current);
|
|
30
|
-
};
|
|
31
|
-
const touch = () => {
|
|
32
|
-
aim.current = void 0;
|
|
33
|
-
};
|
|
34
|
-
const scrolled = (scroller) => {
|
|
35
|
-
setIndex(scrolledPhoto(scroller.scrollLeft, scroller.clientWidth, list.length));
|
|
36
|
-
if (aim.current !== void 0 && Math.abs(scroller.scrollLeft - aim.current * scroller.clientWidth) < 1) aim.current = void 0;
|
|
37
|
-
};
|
|
38
|
-
const picture = /* @__PURE__ */ jsx(CardImageFrame, { children: /* @__PURE__ */ jsx("span", {
|
|
39
|
-
ref: track,
|
|
40
|
-
tabIndex: -1,
|
|
17
|
+
const carousel = useCarousel(list.length);
|
|
18
|
+
const picture = /* @__PURE__ */ jsx(CardImageFrame, { children: /* @__PURE__ */ jsx(CarouselTrack, {
|
|
41
19
|
"data-card-photos": true,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
children: list.map((photo, position) => /* @__PURE__ */ jsx("span", {
|
|
46
|
-
className: "block size-full shrink-0 snap-start snap-always",
|
|
47
|
-
children: /* @__PURE__ */ jsx(CardPhoto, { value: photo })
|
|
48
|
-
}, position))
|
|
20
|
+
carousel,
|
|
21
|
+
swipe,
|
|
22
|
+
slides: list.map((photo) => /* @__PURE__ */ jsx(CardPhoto, { value: photo }))
|
|
49
23
|
}) });
|
|
50
|
-
return /* @__PURE__ */ jsxs(Fragment
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
className: cn(arrow, !swipe && touchArrow, "left-2"),
|
|
57
|
-
onClick: () => press(-1),
|
|
58
|
-
children: /* @__PURE__ */ jsx(ChevronLeft, {
|
|
59
|
-
className: "size-4",
|
|
60
|
-
"aria-hidden": true
|
|
61
|
-
})
|
|
62
|
-
}),
|
|
63
|
-
/* @__PURE__ */ jsx("button", {
|
|
64
|
-
type: "button",
|
|
65
|
-
"aria-label": text("nextPhoto"),
|
|
66
|
-
className: cn(arrow, !swipe && touchArrow, "right-2"),
|
|
67
|
-
onClick: () => press(1),
|
|
68
|
-
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
69
|
-
className: "size-4",
|
|
70
|
-
"aria-hidden": true
|
|
71
|
-
})
|
|
72
|
-
}),
|
|
73
|
-
/* @__PURE__ */ jsxs("span", {
|
|
74
|
-
className: "absolute inset-x-0 bottom-2 flex justify-center",
|
|
75
|
-
"aria-live": "polite",
|
|
76
|
-
children: [/* @__PURE__ */ jsx("span", {
|
|
77
|
-
className: "sr-only",
|
|
78
|
-
children: text("photoOf", index + 1, list.length)
|
|
79
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
80
|
-
className: "flex items-center gap-1 rounded-full bg-background/70 px-1.5 py-1 leading-none",
|
|
81
|
-
"aria-hidden": true,
|
|
82
|
-
children: list.map((_, dot) => /* @__PURE__ */ jsx("span", { className: cn("size-1.5 rounded-full", dot === index ? "bg-foreground" : "bg-foreground/35") }, dot))
|
|
83
|
-
})]
|
|
84
|
-
})
|
|
85
|
-
]
|
|
24
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [children(picture), /* @__PURE__ */ jsx(CarouselControls, {
|
|
25
|
+
carousel,
|
|
26
|
+
count: list.length,
|
|
27
|
+
swipe,
|
|
28
|
+
reveal: "group-has-[[data-card-photos]:hover]/card:opacity-100",
|
|
29
|
+
className: "inset-x-px top-px aspect-[4/3]"
|
|
86
30
|
})] });
|
|
87
31
|
};
|
|
88
32
|
//#endregion
|
|
@@ -12,9 +12,5 @@ const cardPhotoList = (cover, photos) => {
|
|
|
12
12
|
const rest = photos.filter((photo) => isStoredFile(photo) && photo.uri !== coverUri);
|
|
13
13
|
return (coverUri === void 0 ? rest : [cover, ...rest]).slice(0, maxCardDots);
|
|
14
14
|
};
|
|
15
|
-
/** The position shown after `step` presses forward, or back when negative, wrapping around both ends. */
|
|
16
|
-
const shownPhoto = (step, count) => count === 0 ? 0 : (step % count + count) % count;
|
|
17
|
-
/** The position a track of `count` photos, each `width` wide, shows when scrolled to `left`: the one mostly in view. */
|
|
18
|
-
const scrolledPhoto = (left, width, count) => width <= 0 || count === 0 ? 0 : Math.min(count - 1, Math.max(0, Math.round(left / width)));
|
|
19
15
|
//#endregion
|
|
20
|
-
export { cardPhotoList, maxCardDots
|
|
16
|
+
export { cardPhotoList, maxCardDots };
|
|
@@ -44,7 +44,6 @@ const text = defineMessages({
|
|
|
44
44
|
today: "Today",
|
|
45
45
|
capped: (limit, unit) => `Showing the first ${limit} records of this ${unit}. Narrow the filter to see the rest.`,
|
|
46
46
|
nothingIn: (unit) => `Nothing in this ${unit}.`,
|
|
47
|
-
photoOf: (index, count) => `Photo ${index} of ${count}`,
|
|
48
47
|
noneMatch: (plural) => `No ${plural} match.`,
|
|
49
48
|
list: "List",
|
|
50
49
|
showOneApproximate: (singular) => `Show the ${singular}, at an approximate position`,
|
|
@@ -64,9 +63,7 @@ const text = defineMessages({
|
|
|
64
63
|
features: "Features",
|
|
65
64
|
couldNotReorder: "Could not change the order",
|
|
66
65
|
couldNotMoveTo: (to) => `Could not move it to ${to}`,
|
|
67
|
-
moreHidden: (count) => `+${count} more
|
|
68
|
-
previousPhoto: "Previous photo",
|
|
69
|
-
nextPhoto: "Next photo"
|
|
66
|
+
moreHidden: (count) => `+${count} more`
|
|
70
67
|
},
|
|
71
68
|
nl: {
|
|
72
69
|
table: "Tabel",
|
|
@@ -127,7 +124,6 @@ const text = defineMessages({
|
|
|
127
124
|
month: "deze maand",
|
|
128
125
|
quarter: "dit kwartaal"
|
|
129
126
|
}[unit]}.`,
|
|
130
|
-
photoOf: (index, count) => `Foto ${index} van ${count}`,
|
|
131
127
|
noneMatch: (plural) => `Geen ${plural} gevonden.`,
|
|
132
128
|
list: "Lijst",
|
|
133
129
|
showOneApproximate: (singular) => `${singular} tonen, op een geschatte plek`,
|
|
@@ -147,9 +143,7 @@ const text = defineMessages({
|
|
|
147
143
|
features: "Kenmerken",
|
|
148
144
|
couldNotReorder: "Kon de volgorde niet wijzigen",
|
|
149
145
|
couldNotMoveTo: (to) => `Kon het niet naar ${to} verplaatsen`,
|
|
150
|
-
moreHidden: (count) => `+${count} meer
|
|
151
|
-
previousPhoto: "Vorige foto",
|
|
152
|
-
nextPhoto: "Volgende foto"
|
|
146
|
+
moreHidden: (count) => `+${count} meer`
|
|
153
147
|
}
|
|
154
148
|
});
|
|
155
149
|
//#endregion
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { text } from "./messages.js";
|
|
3
|
+
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
//#region src/carousel/carousel-controls.tsx
|
|
6
|
+
const arrow = "pointer-events-auto absolute top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-full bg-background/85 text-foreground shadow-sm opacity-0 transition-opacity group-hover/arrows:opacity-100 group-has-[:focus-visible]/arrows:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30";
|
|
7
|
+
const touchArrow = "[@media(hover:none)]:opacity-100";
|
|
8
|
+
/**
|
|
9
|
+
* The arrows and dots over a track of more than one slide. They sit beside the track in the document, so pressing an
|
|
10
|
+
* arrow inside a link, as a record card's picture is, does not follow it.
|
|
11
|
+
*/
|
|
12
|
+
const CarouselControls = ({ carousel, count, swipe, reveal, className }) => {
|
|
13
|
+
if (count < 2) return null;
|
|
14
|
+
const { index, press } = carousel;
|
|
15
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
16
|
+
className: cn("group/arrows pointer-events-none absolute", className),
|
|
17
|
+
children: [
|
|
18
|
+
/* @__PURE__ */ jsx("button", {
|
|
19
|
+
type: "button",
|
|
20
|
+
"aria-label": text("previousPhoto"),
|
|
21
|
+
className: cn(arrow, reveal, !swipe && touchArrow, "left-2"),
|
|
22
|
+
onClick: () => press(-1),
|
|
23
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, {
|
|
24
|
+
className: "size-4",
|
|
25
|
+
"aria-hidden": true
|
|
26
|
+
})
|
|
27
|
+
}),
|
|
28
|
+
/* @__PURE__ */ jsx("button", {
|
|
29
|
+
type: "button",
|
|
30
|
+
"aria-label": text("nextPhoto"),
|
|
31
|
+
className: cn(arrow, reveal, !swipe && touchArrow, "right-2"),
|
|
32
|
+
onClick: () => press(1),
|
|
33
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {
|
|
34
|
+
className: "size-4",
|
|
35
|
+
"aria-hidden": true
|
|
36
|
+
})
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ jsxs("span", {
|
|
39
|
+
className: "absolute inset-x-0 bottom-2 flex justify-center",
|
|
40
|
+
"aria-live": "polite",
|
|
41
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
42
|
+
className: "sr-only",
|
|
43
|
+
children: text("photoOf", index + 1, count)
|
|
44
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
45
|
+
className: "flex items-center gap-1 rounded-full bg-background/70 px-1.5 py-1 leading-none",
|
|
46
|
+
"aria-hidden": true,
|
|
47
|
+
children: Array.from({ length: count }, (_, dot) => /* @__PURE__ */ jsx("span", { className: cn("size-1.5 rounded-full", dot === index ? "bg-foreground" : "bg-foreground/35") }, dot))
|
|
48
|
+
})]
|
|
49
|
+
})
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { CarouselControls };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { StoredFile } from "@protobase/client";
|
|
2
|
+
//#region src/carousel/carousel-images.d.ts
|
|
3
|
+
/** An image a carousel shows: a link with what it shows, or a file field's value, as records carry it. */
|
|
4
|
+
export type CarouselImage = {
|
|
5
|
+
src: string;
|
|
6
|
+
alt: string;
|
|
7
|
+
} | (StoredFile & {
|
|
8
|
+
alt?: string;
|
|
9
|
+
});
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { shownImage } from "../live/file-values.js";
|
|
2
|
+
//#region src/carousel/carousel-images.ts
|
|
3
|
+
const box = {
|
|
4
|
+
width: 640,
|
|
5
|
+
height: 480
|
|
6
|
+
};
|
|
7
|
+
/** What each slide draws, in order: a file that is missing or is no raster image is left out. */
|
|
8
|
+
const carouselSlides = (images) => images.flatMap((image) => {
|
|
9
|
+
if ("src" in image) return [{
|
|
10
|
+
image: { url: image.src },
|
|
11
|
+
alt: image.alt
|
|
12
|
+
}];
|
|
13
|
+
const shown = shownImage(image, box);
|
|
14
|
+
return shown ? [{
|
|
15
|
+
image: shown,
|
|
16
|
+
alt: image.alt ?? ""
|
|
17
|
+
}] : [];
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
20
|
+
export { carouselSlides };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
//#region src/carousel/carousel-steps.ts
|
|
2
|
+
/** The position shown after `step` presses forward, or back when negative, wrapping around both ends. */
|
|
3
|
+
const shownSlide = (step, count) => count === 0 ? 0 : (step % count + count) % count;
|
|
4
|
+
/** The position a track of `count` slides, each `width` wide, shows when scrolled to `left`: the one mostly in view. */
|
|
5
|
+
const scrolledSlide = (left, width, count) => width <= 0 || count === 0 ? 0 : Math.min(count - 1, Math.max(0, Math.round(left / width)));
|
|
6
|
+
//#endregion
|
|
7
|
+
export { scrolledSlide, shownSlide };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
//#region src/carousel/carousel-track.tsx
|
|
4
|
+
/** The slides side by side in a row that snaps slide by slide and hides its scrollbar. */
|
|
5
|
+
const CarouselTrack = ({ carousel, swipe, slides, className, ...rest }) => /* @__PURE__ */ jsx("span", {
|
|
6
|
+
...rest,
|
|
7
|
+
ref: carousel.track,
|
|
8
|
+
tabIndex: -1,
|
|
9
|
+
onPointerDown: carousel.touch,
|
|
10
|
+
onScroll: (event) => carousel.scrolled(event.currentTarget),
|
|
11
|
+
className: cn("flex size-full snap-x snap-mandatory outline-none [scrollbar-width:none] motion-safe:scroll-smooth [&::-webkit-scrollbar]:hidden", swipe ? "overflow-x-auto overscroll-x-contain" : "overflow-x-hidden", className),
|
|
12
|
+
children: slides.map((slide, position) => /* @__PURE__ */ jsx("span", {
|
|
13
|
+
className: "block size-full shrink-0 snap-start snap-always",
|
|
14
|
+
children: slide
|
|
15
|
+
}, position))
|
|
16
|
+
});
|
|
17
|
+
//#endregion
|
|
18
|
+
export { CarouselTrack };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CarouselImage } from "./carousel-images.js";
|
|
2
|
+
//#region src/carousel/carousel.d.ts
|
|
3
|
+
export type CarouselProps = {
|
|
4
|
+
/** The images in order: links with what each shows, or file field values. */
|
|
5
|
+
images: readonly CarouselImage[];
|
|
6
|
+
/** What the images are, read out with the carousel. */
|
|
7
|
+
label?: string;
|
|
8
|
+
/** `cover` fills the frame and crops what overflows it, the default; `contain` shows each image whole. */
|
|
9
|
+
fit?: 'cover' | 'contain';
|
|
10
|
+
/** Whether a swipe steps through the images; off, it scrolls what the carousel sits in, such as a row that scrolls sideways. */
|
|
11
|
+
swipe?: boolean;
|
|
12
|
+
/** The frame's classes, such as its aspect ratio, border and rounding. */
|
|
13
|
+
className?: string;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Images side by side in a frame that snaps image by image, swiped on a touch screen or moved by arrows on hover, with
|
|
17
|
+
* dots marking the one in view; the record cards' photos step the same way. A single image shows without arrows or dots.
|
|
18
|
+
*/
|
|
19
|
+
export declare const Carousel: ({ images, label, fit, swipe, className }: CarouselProps) => import("react").JSX.Element;
|
|
20
|
+
//#endregion
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { cn } from "../lib/cn.js";
|
|
2
|
+
import { FileImage } from "../live/file-image.js";
|
|
3
|
+
import { CarouselControls } from "./carousel-controls.js";
|
|
4
|
+
import { CarouselTrack } from "./carousel-track.js";
|
|
5
|
+
import { useCarousel } from "./use-carousel.js";
|
|
6
|
+
import { carouselSlides } from "./carousel-images.js";
|
|
7
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
+
//#region src/carousel/carousel.tsx
|
|
9
|
+
/**
|
|
10
|
+
* Images side by side in a frame that snaps image by image, swiped on a touch screen or moved by arrows on hover, with
|
|
11
|
+
* dots marking the one in view; the record cards' photos step the same way. A single image shows without arrows or dots.
|
|
12
|
+
*/
|
|
13
|
+
const Carousel = ({ images, label, fit = "cover", swipe = true, className }) => {
|
|
14
|
+
const slides = carouselSlides(images);
|
|
15
|
+
const carousel = useCarousel(slides.length);
|
|
16
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
17
|
+
role: "group",
|
|
18
|
+
"aria-roledescription": "carousel",
|
|
19
|
+
"aria-label": label,
|
|
20
|
+
className: cn("group/carousel relative overflow-hidden", className),
|
|
21
|
+
children: [/* @__PURE__ */ jsx(CarouselTrack, {
|
|
22
|
+
"data-carousel-track": true,
|
|
23
|
+
carousel,
|
|
24
|
+
swipe,
|
|
25
|
+
slides: slides.map(({ image, alt }) => /* @__PURE__ */ jsx(FileImage, {
|
|
26
|
+
image,
|
|
27
|
+
alt,
|
|
28
|
+
loading: "lazy",
|
|
29
|
+
className: cn("size-full", fit === "cover" ? "object-cover" : "object-contain")
|
|
30
|
+
}))
|
|
31
|
+
}), /* @__PURE__ */ jsx(CarouselControls, {
|
|
32
|
+
carousel,
|
|
33
|
+
count: slides.length,
|
|
34
|
+
swipe,
|
|
35
|
+
reveal: "group-hover/carousel:opacity-100",
|
|
36
|
+
className: "inset-0"
|
|
37
|
+
})]
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
//#endregion
|
|
41
|
+
export { Carousel };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { defineMessages } from "../locale/messages.js";
|
|
2
|
+
//#region src/carousel/messages.ts
|
|
3
|
+
const text = defineMessages({
|
|
4
|
+
en: {
|
|
5
|
+
photoOf: (index, count) => `Photo ${index} of ${count}`,
|
|
6
|
+
previousPhoto: "Previous photo",
|
|
7
|
+
nextPhoto: "Next photo"
|
|
8
|
+
},
|
|
9
|
+
nl: {
|
|
10
|
+
photoOf: (index, count) => `Foto ${index} van ${count}`,
|
|
11
|
+
previousPhoto: "Vorige foto",
|
|
12
|
+
nextPhoto: "Volgende foto"
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { text };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { scrolledSlide, shownSlide } from "./carousel-steps.js";
|
|
2
|
+
import { useRef, useState } from "react";
|
|
3
|
+
//#region src/carousel/use-carousel.ts
|
|
4
|
+
/**
|
|
5
|
+
* A track of `count` slides that snaps slide by slide: the slide in view, a press of an arrow, and the track's handlers
|
|
6
|
+
* that follow a swipe.
|
|
7
|
+
*/
|
|
8
|
+
const useCarousel = (count) => {
|
|
9
|
+
const track = useRef(null);
|
|
10
|
+
const aim = useRef(void 0);
|
|
11
|
+
const [index, setIndex] = useState(0);
|
|
12
|
+
const show = (position) => {
|
|
13
|
+
const scroller = track.current;
|
|
14
|
+
if (scroller) scroller.scrollTo({ left: position * scroller.clientWidth });
|
|
15
|
+
};
|
|
16
|
+
const press = (by) => {
|
|
17
|
+
aim.current = shownSlide((aim.current ?? index) + by, count);
|
|
18
|
+
show(aim.current);
|
|
19
|
+
};
|
|
20
|
+
const touch = () => {
|
|
21
|
+
aim.current = void 0;
|
|
22
|
+
};
|
|
23
|
+
const scrolled = (scroller) => {
|
|
24
|
+
setIndex(scrolledSlide(scroller.scrollLeft, scroller.clientWidth, count));
|
|
25
|
+
if (aim.current !== void 0 && Math.abs(scroller.scrollLeft - aim.current * scroller.clientWidth) < 1) aim.current = void 0;
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
track,
|
|
29
|
+
index,
|
|
30
|
+
press,
|
|
31
|
+
touch,
|
|
32
|
+
scrolled
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
//#endregion
|
|
36
|
+
export { useCarousel };
|
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,9 @@ import { ActionCard, ActionCardProps, CardAction, CardTone } from "./action-card
|
|
|
47
47
|
import { FieldList } from "./action-card/field-list.js";
|
|
48
48
|
import "./action-card/index.js";
|
|
49
49
|
import { Step, StepList, StepState } from "./step-list/step-list.js";
|
|
50
|
+
import { CarouselImage } from "./carousel/carousel-images.js";
|
|
51
|
+
import { Carousel, CarouselProps } from "./carousel/carousel.js";
|
|
52
|
+
import "./carousel/index.js";
|
|
50
53
|
import { DiffView, DiffViewProps } from "./diff-view/diff-view.js";
|
|
51
54
|
import { DiffLine, diffStats, parseDiff } from "./diff-view/diff.js";
|
|
52
55
|
import { CompactTable, CompactTableProps } from "./compact-table/compact-table.js";
|
|
@@ -69,4 +72,4 @@ import { Section } from "./record-view/section.js";
|
|
|
69
72
|
import { Form, FormProps } from "./app/create/form.js";
|
|
70
73
|
import { RecordPicker, RecordPickerProps } from "./app/record-picker.js";
|
|
71
74
|
import { Spinner } from "./primitives/spinner.js";
|
|
72
|
-
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, allowDelayMs, buttonClass, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
|
|
75
|
+
export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ActionInputError, type ActionInputProps, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, type BoardDrop, type BoardDropContext, type BoardDropUndo, type BoardDropWrite, Button, type ButtonProps, type CardAction, type CardTone, Carousel, type CarouselImage, type CarouselProps, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, type Drop, type DropRule, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, Markdown, type MarkdownContentProps as MarkdownProps, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type PushState, type RecordCardProps, RecordLink, RecordPicker, type RecordPickerProps, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, allowDelayMs, buttonClass, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
|
package/dist/index.js
CHANGED
|
@@ -53,6 +53,7 @@ import { useInbox, useMarkNotificationsRead, useUnreadNotifications } from "./no
|
|
|
53
53
|
import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
|
|
54
54
|
import { App } from "./app/app.js";
|
|
55
55
|
import { RecordLink } from "./app/displays/record-link.js";
|
|
56
|
+
import { Carousel } from "./carousel/carousel.js";
|
|
56
57
|
import { Choices } from "./primitives/choices.js";
|
|
57
58
|
import { RecordPicker } from "./app/record-picker.js";
|
|
58
|
-
export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
|
|
59
|
+
export { ActionCard, ActionInputError, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, Carousel, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, Markdown, MenuSelect, OtpInput, PartView, RecordLink, RecordPicker, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, allowDelayMs, buttonClass, definePlugin, defineUi, diffStats, isConflict, isFramed, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useArmedAfterFocus, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useFieldControl, useGuardedPress, useHistogram, useInbox, useLayoutRecord, useList, useListDisplay, useMarkNotificationsRead, useMeta, usePlugin, usePushNotifications, useRecord, useResourceClient, useSeries, useUnreadNotifications, useUpdateRecord };
|
package/dist/live/file-image.js
CHANGED
|
@@ -18,14 +18,14 @@ const placeholderImage = (hash) => {
|
|
|
18
18
|
* An image of a file, its placeholder drawn blurred behind it until it has loaded. One from another site than the app's
|
|
19
19
|
* own and its trusted hosts loads without a referrer.
|
|
20
20
|
*/
|
|
21
|
-
const FileImage = ({ image, className, loading }) => {
|
|
21
|
+
const FileImage = ({ image, alt = "", className, loading }) => {
|
|
22
22
|
const [loaded, setLoaded] = useState();
|
|
23
23
|
const trustedHosts = useTrustedImageHosts();
|
|
24
24
|
const placeholder = useMemo(() => image.placeholder === void 0 ? void 0 : placeholderImage(image.placeholder), [image.placeholder]);
|
|
25
25
|
const waiting = placeholder !== void 0 && loaded !== image.url;
|
|
26
26
|
return /* @__PURE__ */ jsx("img", {
|
|
27
27
|
src: image.url,
|
|
28
|
-
alt
|
|
28
|
+
alt,
|
|
29
29
|
loading,
|
|
30
30
|
decoding: "async",
|
|
31
31
|
referrerPolicy: imageReferrerPolicy(image.url, window.location.href, trustedHosts),
|
package/dist/package.js
CHANGED