@exponential_app/eui-react 0.3.0 → 0.4.0

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 CHANGED
@@ -331,7 +331,7 @@ A step = the scroll, the window's render flushed and the layout read back
331
331
  | item | web |
332
332
  |---|---|
333
333
  | macros | `Outline` (the anchor list: ONE active row, its leading bar absolute at `inset-inline-start: 0`, `select {id}`) and `Mosaic` (the wide picture group: `aspect-ratio: 1.6`, the 60 % lead, cover pictures absolute `inset: 0`, "+N") need no painter |
334
- | Carousel `counter` | 2+ pages: the `counter` part (`$string.pageCounter`, `i / n`) absolute at the track's top-start corner inside a `relative` stage (so the pill stays while the track scrolls), `pointer-events: none`, `aria-hidden` |
334
+ | Carousel `counter` | 2+ pages: the `counter` part (`$string.pageCounter`, `i / n`) absolute at the track's top-end corner (mirrored under RTL) inside a `relative` stage (so the pill stays while the track scrolls), `pointer-events: none`, `aria-hidden` |
335
335
  | Carousel `thumbnails` | 2+ pages: the `thumbnails` strip between the track and the controls, ONE `thumbnail` button per page (`<id>.thumbnail.<i>`; URLs past the page count ignored, a missing one = the empty muted square), the current one `selected` + `aria-current` + `data-current`, named `$string.pageOf`; a press pages and fires `change {page}` like a dot; the current one is scrolled into view inside the strip |
336
336
  | primitives | `Outline`, `Mosaic`, `CarouselCounter`, `CarouselThumbnails` on `OUTLINE_CLASSES` / `MOSAIC_CLASSES` / `CAROUSEL_CLASSES` (specimens `plain/outline`, `plain/mosaic`, `plain/carousel-extras`) |
337
337
 
@@ -0,0 +1,596 @@
1
+ // packages/exponential-ui-react/src/primitives/choice-tiles.tsx
2
+ import * as React from "react";
3
+ import { ImageIcon } from "lucide-react";
4
+ import { CHOICE_TILES_CLASSES, choiceColumns, choiceToggle, choiceValues } from "@exponential_app/eui/web/primitives";
5
+
6
+ // packages/exponential-ui-react/src/primitives/cn.ts
7
+ import { cn as cn2, getInitials as getInitials2 } from "@exponential_app/eui/web/primitives";
8
+
9
+ // packages/exponential-ui-react/src/primitives/choice-tiles.tsx
10
+ import { choiceColumns as choiceColumns2, choiceToggle as choiceToggle2, choiceValues as choiceValues2 } from "@exponential_app/eui/web/primitives";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
12
+ function ChoiceTilesFrame2({ options, picked, multiple, columns, disabled = false, invalid = false, dir = `ltr`, onPick, rootProps, tileProps, pictureProps, labelProps, descriptionProps, renderPicture, after, rootRef }) {
13
+ const buttons = React.useRef([]);
14
+ const order = React.useMemo(() => options.map((o) => o.value), [options]);
15
+ const pictures = options.some((o) => Boolean(o.image));
16
+ const enabled = (i) => !disabled && !options[i]?.disabled;
17
+ const firstPicked = options.findIndex((o, i) => picked.includes(o.value) && enabled(i));
18
+ const tabStop = firstPicked >= 0 ? firstPicked : options.findIndex((_, i) => enabled(i));
19
+ const pick = (i) => {
20
+ const value = options[i].value;
21
+ onPick(choiceToggle(picked, value, multiple, order), value);
22
+ };
23
+ const move = (from, delta) => {
24
+ const n = options.length;
25
+ for (let step = 1;step <= n; step++) {
26
+ const i = ((from + delta * step) % n + n) % n;
27
+ if (enabled(i))
28
+ return i;
29
+ }
30
+ return -1;
31
+ };
32
+ const onKeyDown = (i) => (e) => {
33
+ const forward = dir === `rtl` ? `ArrowLeft` : `ArrowRight`;
34
+ const back = dir === `rtl` ? `ArrowRight` : `ArrowLeft`;
35
+ const delta = e.key === forward || e.key === `ArrowDown` ? 1 : e.key === back || e.key === `ArrowUp` ? -1 : 0;
36
+ if (delta === 0)
37
+ return;
38
+ e.preventDefault();
39
+ const next = move(i, delta);
40
+ if (next < 0)
41
+ return;
42
+ buttons.current[next]?.focus();
43
+ if (!multiple && !picked.includes(options[next].value))
44
+ pick(next);
45
+ };
46
+ return /* @__PURE__ */ jsxs("div", {
47
+ ref: rootRef,
48
+ role: multiple ? `group` : `radiogroup`,
49
+ "aria-invalid": invalid || undefined,
50
+ "aria-disabled": disabled || undefined,
51
+ dir,
52
+ ...rootProps,
53
+ style: { display: `grid`, gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`, ...rootProps?.style ?? {} },
54
+ children: [
55
+ options.map((o, i) => {
56
+ const state = { index: i, selected: picked.includes(o.value), disabled: !enabled(i) };
57
+ const description = o.description ? String(o.description) : ``;
58
+ return /* @__PURE__ */ jsxs("button", {
59
+ ref: (el) => {
60
+ buttons.current[i] = el;
61
+ },
62
+ type: "button",
63
+ role: multiple ? `checkbox` : `radio`,
64
+ "aria-checked": state.selected,
65
+ "aria-label": o.label,
66
+ disabled: state.disabled,
67
+ tabIndex: i === tabStop ? 0 : -1,
68
+ "data-state": state.selected ? `checked` : `unchecked`,
69
+ "data-value": o.value,
70
+ onClick: () => pick(i),
71
+ onKeyDown: onKeyDown(i),
72
+ ...tileProps(state),
73
+ children: [
74
+ pictures ? /* @__PURE__ */ jsx("span", {
75
+ "aria-hidden": "true",
76
+ ...pictureProps(state),
77
+ children: renderPicture(o.image ? String(o.image) : null, state)
78
+ }) : null,
79
+ /* @__PURE__ */ jsx("span", {
80
+ ...labelProps(state),
81
+ children: o.label
82
+ }),
83
+ description ? /* @__PURE__ */ jsx("span", {
84
+ ...descriptionProps(state),
85
+ children: description
86
+ }) : null
87
+ ]
88
+ }, `${o.value}-${i}`);
89
+ }),
90
+ after
91
+ ]
92
+ });
93
+ }
94
+ function ChoiceTiles2({ options, value, multiple = false, columns, disabled = false, invalid = false, onChange, renderImage, className, dir, ...rest }) {
95
+ const picked = choiceValues(value, multiple);
96
+ const image = (src, label) => src ? renderImage ? renderImage(src, label) : /* @__PURE__ */ jsx("img", {
97
+ src,
98
+ alt: label,
99
+ loading: "lazy",
100
+ decoding: "async",
101
+ className: CHOICE_TILES_CLASSES.image
102
+ }) : /* @__PURE__ */ jsx(ImageIcon, {
103
+ className: CHOICE_TILES_CLASSES.placeholder
104
+ });
105
+ return /* @__PURE__ */ jsx(ChoiceTilesFrame2, {
106
+ options,
107
+ picked,
108
+ multiple,
109
+ columns: choiceColumns(columns),
110
+ disabled,
111
+ invalid,
112
+ dir,
113
+ onPick: (next) => onChange?.(multiple ? next : next[0] ?? ``),
114
+ rootProps: { "data-slot": `choice-tiles`, className: cn2(CHOICE_TILES_CLASSES.root, className), "aria-label": rest[`aria-label`], "aria-labelledby": rest[`aria-labelledby`], "data-testid": rest[`data-testid`] },
115
+ tileProps: (s) => ({ "data-slot": `choice-tile`, className: cn2(CHOICE_TILES_CLASSES.tile, s.selected && CHOICE_TILES_CLASSES.tileSelected, invalid && !s.selected && CHOICE_TILES_CLASSES.tileInvalid) }),
116
+ pictureProps: () => ({ "data-slot": `choice-tile-picture`, className: CHOICE_TILES_CLASSES.picture }),
117
+ labelProps: () => ({ className: CHOICE_TILES_CLASSES.label }),
118
+ descriptionProps: () => ({ className: CHOICE_TILES_CLASSES.description }),
119
+ renderPicture: (src, s) => image(src, options[s.index]?.label ?? ``)
120
+ });
121
+ }
122
+
123
+ // packages/exponential-ui-react/src/primitives/zoomable-image.tsx
124
+ import { useEffect, useLayoutEffect, useRef as useRef2, useState } from "react";
125
+ import { Maximize2Icon, MinusIcon, PlusIcon } from "lucide-react";
126
+ import {
127
+ ZOOM,
128
+ zoomAtRest,
129
+ zoomBounds,
130
+ zoomCentre,
131
+ zoomClamp,
132
+ zoomDoubleTap,
133
+ zoomDrag,
134
+ zoomIsTall,
135
+ zoomKey,
136
+ zoomMenu,
137
+ zoomMomentum,
138
+ zoomMomentumActive,
139
+ zoomPanRoom,
140
+ zoomPercent,
141
+ zoomPinch,
142
+ zoomResize,
143
+ zoomRestView,
144
+ zoomScroll,
145
+ zoomStep,
146
+ zoomTo,
147
+ zoomWheel
148
+ } from "@exponential_app/eui";
149
+ import {
150
+ ZOOMABLE_IMAGE_CLASSES,
151
+ ZOOMABLE_IMAGE_TRANSITION,
152
+ createZoomController,
153
+ zoomBoxAspect,
154
+ zoomDeclaredSize,
155
+ zoomImageStyle
156
+ } from "@exponential_app/eui/web/primitives";
157
+
158
+ // packages/exponential-ui-react/src/primitives/button.tsx
159
+ import { Slot } from "radix-ui";
160
+ import { buttonVariants as buttonVariants2 } from "@exponential_app/eui/web/primitives";
161
+ import { jsx as jsx2 } from "react/jsx-runtime";
162
+ function Button2({
163
+ className,
164
+ variant = `default`,
165
+ size = `default`,
166
+ asChild = false,
167
+ ...props
168
+ }) {
169
+ const Comp = asChild ? Slot.Root : `button`;
170
+ return /* @__PURE__ */ jsx2(Comp, {
171
+ "data-slot": "button",
172
+ "data-variant": variant,
173
+ "data-size": size,
174
+ className: cn2(buttonVariants2({ variant, size, className })),
175
+ ...props
176
+ });
177
+ }
178
+
179
+ // packages/exponential-ui-react/src/primitives/dropdown-menu.tsx
180
+ import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
181
+ import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui";
182
+ import { DROPDOWN_MENU_CLASSES } from "@exponential_app/eui/web/primitives";
183
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
184
+ "use client";
185
+ function DropdownMenu2({
186
+ ...props
187
+ }) {
188
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Root, {
189
+ "data-slot": "dropdown-menu",
190
+ ...props
191
+ });
192
+ }
193
+ function DropdownMenuPortal2({
194
+ ...props
195
+ }) {
196
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Portal, {
197
+ "data-slot": "dropdown-menu-portal",
198
+ ...props
199
+ });
200
+ }
201
+ function DropdownMenuTrigger2({
202
+ ...props
203
+ }) {
204
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Trigger, {
205
+ "data-slot": "dropdown-menu-trigger",
206
+ ...props
207
+ });
208
+ }
209
+ function DropdownMenuContent2({
210
+ className,
211
+ sideOffset = 4,
212
+ ...props
213
+ }) {
214
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Portal, {
215
+ children: /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Content, {
216
+ "data-slot": "dropdown-menu-content",
217
+ sideOffset,
218
+ className: cn2(DROPDOWN_MENU_CLASSES.content, className),
219
+ ...props
220
+ })
221
+ });
222
+ }
223
+ function DropdownMenuGroup2({
224
+ ...props
225
+ }) {
226
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Group, {
227
+ "data-slot": "dropdown-menu-group",
228
+ ...props
229
+ });
230
+ }
231
+ function DropdownMenuItem2({
232
+ className,
233
+ inset,
234
+ variant = `default`,
235
+ ...props
236
+ }) {
237
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Item, {
238
+ "data-slot": "dropdown-menu-item",
239
+ "data-inset": inset,
240
+ "data-variant": variant,
241
+ className: cn2(DROPDOWN_MENU_CLASSES.item, className),
242
+ ...props
243
+ });
244
+ }
245
+ function DropdownMenuCheckboxItem2({
246
+ className,
247
+ children,
248
+ checked,
249
+ ...props
250
+ }) {
251
+ return /* @__PURE__ */ jsxs2(DropdownMenuPrimitive.CheckboxItem, {
252
+ "data-slot": "dropdown-menu-checkbox-item",
253
+ className: cn2(DROPDOWN_MENU_CLASSES.checkItem, className),
254
+ checked,
255
+ ...props,
256
+ children: [
257
+ /* @__PURE__ */ jsx3("span", {
258
+ className: DROPDOWN_MENU_CLASSES.itemIndicator,
259
+ children: /* @__PURE__ */ jsx3(DropdownMenuPrimitive.ItemIndicator, {
260
+ children: /* @__PURE__ */ jsx3(CheckIcon, {
261
+ className: DROPDOWN_MENU_CLASSES.checkIcon
262
+ })
263
+ })
264
+ }),
265
+ children
266
+ ]
267
+ });
268
+ }
269
+ function DropdownMenuRadioGroup2({
270
+ ...props
271
+ }) {
272
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.RadioGroup, {
273
+ "data-slot": "dropdown-menu-radio-group",
274
+ ...props
275
+ });
276
+ }
277
+ function DropdownMenuRadioItem2({
278
+ className,
279
+ children,
280
+ ...props
281
+ }) {
282
+ return /* @__PURE__ */ jsxs2(DropdownMenuPrimitive.RadioItem, {
283
+ "data-slot": "dropdown-menu-radio-item",
284
+ className: cn2(DROPDOWN_MENU_CLASSES.checkItem, className),
285
+ ...props,
286
+ children: [
287
+ /* @__PURE__ */ jsx3("span", {
288
+ className: DROPDOWN_MENU_CLASSES.itemIndicator,
289
+ children: /* @__PURE__ */ jsx3(DropdownMenuPrimitive.ItemIndicator, {
290
+ children: /* @__PURE__ */ jsx3(CircleIcon, {
291
+ className: DROPDOWN_MENU_CLASSES.radioIcon
292
+ })
293
+ })
294
+ }),
295
+ children
296
+ ]
297
+ });
298
+ }
299
+ function DropdownMenuLabel2({
300
+ className,
301
+ inset,
302
+ ...props
303
+ }) {
304
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Label, {
305
+ "data-slot": "dropdown-menu-label",
306
+ "data-inset": inset,
307
+ className: cn2(DROPDOWN_MENU_CLASSES.label, className),
308
+ ...props
309
+ });
310
+ }
311
+ function DropdownMenuSeparator2({
312
+ className,
313
+ ...props
314
+ }) {
315
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Separator, {
316
+ "data-slot": "dropdown-menu-separator",
317
+ className: cn2(DROPDOWN_MENU_CLASSES.separator, className),
318
+ ...props
319
+ });
320
+ }
321
+ function DropdownMenuShortcut2({
322
+ className,
323
+ ...props
324
+ }) {
325
+ return /* @__PURE__ */ jsx3("span", {
326
+ "data-slot": "dropdown-menu-shortcut",
327
+ className: cn2(DROPDOWN_MENU_CLASSES.shortcut, className),
328
+ ...props
329
+ });
330
+ }
331
+ function DropdownMenuSub2({
332
+ ...props
333
+ }) {
334
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Sub, {
335
+ "data-slot": "dropdown-menu-sub",
336
+ ...props
337
+ });
338
+ }
339
+ function DropdownMenuSubTrigger2({
340
+ className,
341
+ inset,
342
+ children,
343
+ variant = `default`,
344
+ ...props
345
+ }) {
346
+ return /* @__PURE__ */ jsxs2(DropdownMenuPrimitive.SubTrigger, {
347
+ "data-slot": "dropdown-menu-sub-trigger",
348
+ "data-inset": inset,
349
+ "data-variant": variant,
350
+ className: cn2(DROPDOWN_MENU_CLASSES.subTrigger, className),
351
+ ...props,
352
+ children: [
353
+ children,
354
+ /* @__PURE__ */ jsx3(ChevronRightIcon, {
355
+ className: DROPDOWN_MENU_CLASSES.subChevron
356
+ })
357
+ ]
358
+ });
359
+ }
360
+ function DropdownMenuSubContent2({
361
+ className,
362
+ ...props
363
+ }) {
364
+ return /* @__PURE__ */ jsx3(DropdownMenuPrimitive.Portal, {
365
+ children: /* @__PURE__ */ jsx3(DropdownMenuPrimitive.SubContent, {
366
+ "data-slot": "dropdown-menu-sub-content",
367
+ className: cn2(DROPDOWN_MENU_CLASSES.subContent, className),
368
+ ...props
369
+ })
370
+ });
371
+ }
372
+
373
+ // packages/exponential-ui-react/src/primitives/use-media-query.ts
374
+ import { useCallback, useSyncExternalStore } from "react";
375
+ function useMediaQuery2(query) {
376
+ const subscribe = useCallback((onChange) => {
377
+ if (typeof window === `undefined` || typeof window.matchMedia !== `function`)
378
+ return () => {};
379
+ const mql = window.matchMedia(query);
380
+ mql.addEventListener(`change`, onChange);
381
+ return () => mql.removeEventListener(`change`, onChange);
382
+ }, [query]);
383
+ const read = () => typeof window !== `undefined` && typeof window.matchMedia === `function` && window.matchMedia(query).matches;
384
+ return useSyncExternalStore(subscribe, read, () => false);
385
+ }
386
+
387
+ // packages/exponential-ui-react/src/primitives/zoomable-image.tsx
388
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
389
+ var ZOOM_MATH2 = {
390
+ ZOOM,
391
+ zoomIsTall,
392
+ zoomRestView,
393
+ zoomClamp,
394
+ zoomAtRest,
395
+ zoomTo,
396
+ zoomCentre,
397
+ zoomWheel,
398
+ zoomScroll,
399
+ zoomStep,
400
+ zoomDoubleTap,
401
+ zoomKey,
402
+ zoomDrag,
403
+ zoomPinch,
404
+ zoomMomentum,
405
+ zoomMomentumActive,
406
+ zoomPanRoom,
407
+ zoomResize,
408
+ zoomPercent,
409
+ zoomMenu,
410
+ zoomBounds
411
+ };
412
+ var REST_PILL = { percent: zoomPercent(1), atMin: false, atMax: false, atRest: true };
413
+ function useZoomableImage2(o) {
414
+ const rootRef = useRef2(null);
415
+ const imageRef = useRef2(null);
416
+ const pillRef = useRef2(null);
417
+ const controller = useRef2(null);
418
+ const [loaded, setLoaded] = useState(null);
419
+ const [pill, setPill] = useState(REST_PILL);
420
+ const [pillVisible, setPillVisible] = useState(true);
421
+ const declared = zoomDeclaredSize(o.naturalWidth, o.naturalHeight);
422
+ const natural = declared ?? loaded;
423
+ const latest = useRef2({ ...o, natural });
424
+ latest.current = { ...o, natural };
425
+ useLayoutEffect(() => {
426
+ const root = rootRef.current;
427
+ const image = imageRef.current;
428
+ if (!root || !image)
429
+ return;
430
+ const c = createZoomController({
431
+ math: ZOOM_MATH2,
432
+ root,
433
+ image,
434
+ pill: () => pillRef.current,
435
+ natural: () => latest.current.natural,
436
+ initial: () => latest.current.initial,
437
+ maxScale: () => latest.current.maxScale,
438
+ boundScale: () => latest.current.scale,
439
+ transition: () => latest.current.transition,
440
+ autoHide: () => latest.current.controls === `auto`,
441
+ onPill: setPill,
442
+ onPillVisible: setPillVisible,
443
+ onZoom: (scale, atFit) => latest.current.onZoom?.(scale, atFit),
444
+ onTap: () => latest.current.onTap?.()
445
+ });
446
+ controller.current = c;
447
+ return () => {
448
+ c.destroy();
449
+ controller.current = null;
450
+ };
451
+ }, []);
452
+ useLayoutEffect(() => setLoaded(null), [o.src]);
453
+ useLayoutEffect(() => controller.current?.reset(), [o.src, natural?.width, natural?.height, o.initial, o.maxScale]);
454
+ useLayoutEffect(() => controller.current?.syncScale(), [o.scale]);
455
+ const onImageLoad = (e) => {
456
+ const img = e.currentTarget;
457
+ if (!declared && img.naturalWidth > 0 && img.naturalHeight > 0)
458
+ setLoaded({ width: img.naturalWidth, height: img.naturalHeight });
459
+ };
460
+ const menuItems = (fitLabel) => {
461
+ const c = controller.current;
462
+ const f = c?.frame();
463
+ return c && f ? zoomMenu(c.view(), f, fitLabel) : [];
464
+ };
465
+ return { rootRef, imageRef, pillRef, controller, natural, pill, pillVisible, onImageLoad, menuItems };
466
+ }
467
+ var ZOOMABLE_IMAGE_STRINGS2 = { zoomIn: `Zoom in`, zoomOut: `Zoom out`, zoomToFit: `Zoom to fit`, zoomLevel: `Zoom level`, zoomFit: `Fit` };
468
+ function ZoomableImage2({ src, alt, naturalWidth, naturalHeight, initial = `auto`, maxScale, controls = `auto`, scale, onScaleChange, onZoom, onTap, strings, className, style, ...props }) {
469
+ const reducedMotion = useMediaQuery2(`(prefers-reduced-motion: reduce)`);
470
+ const t = { ...ZOOMABLE_IMAGE_STRINGS2, ...strings };
471
+ const zoom = useZoomableImage2({
472
+ src,
473
+ naturalWidth,
474
+ naturalHeight,
475
+ initial,
476
+ maxScale,
477
+ scale,
478
+ controls,
479
+ transition: reducedMotion ? null : ZOOMABLE_IMAGE_TRANSITION,
480
+ onZoom: (s, atFit) => {
481
+ onScaleChange?.(s);
482
+ onZoom?.({ scale: s, atFit });
483
+ },
484
+ onTap
485
+ });
486
+ const [menuOpen, setMenuOpen] = useState(false);
487
+ useEffect(() => zoom.controller.current?.holdPill(menuOpen), [menuOpen]);
488
+ const aspect = zoomBoxAspect(ZOOM_MATH2, zoom.natural, style?.height !== undefined);
489
+ const rootStyle = { ...aspect !== null ? { aspectRatio: String(aspect) } : {}, ...style };
490
+ const { pill } = zoom;
491
+ return /* @__PURE__ */ jsxs3("div", {
492
+ ref: zoom.rootRef,
493
+ "data-slot": "zoomable-image",
494
+ role: "group",
495
+ "aria-label": alt || undefined,
496
+ tabIndex: 0,
497
+ className: cn2(ZOOMABLE_IMAGE_CLASSES.root, className),
498
+ style: rootStyle,
499
+ ...props,
500
+ children: [
501
+ /* @__PURE__ */ jsx4("img", {
502
+ ref: zoom.imageRef,
503
+ "data-slot": "zoomable-image-image",
504
+ src: src || undefined,
505
+ alt,
506
+ draggable: false,
507
+ loading: "lazy",
508
+ decoding: "async",
509
+ className: ZOOMABLE_IMAGE_CLASSES.image,
510
+ style: zoomImageStyle(zoom.natural),
511
+ onLoad: zoom.onImageLoad
512
+ }),
513
+ controls !== `none` ? /* @__PURE__ */ jsxs3("div", {
514
+ ref: zoom.pillRef,
515
+ "data-slot": "zoomable-image-pill",
516
+ "data-state": zoom.pillVisible ? `open` : `closed`,
517
+ role: "toolbar",
518
+ className: cn2(ZOOMABLE_IMAGE_CLASSES.pill, !zoom.pillVisible && ZOOMABLE_IMAGE_CLASSES.pillHidden),
519
+ children: [
520
+ /* @__PURE__ */ jsx4(Button2, {
521
+ type: "button",
522
+ variant: "ghost",
523
+ size: "icon-sm",
524
+ className: ZOOMABLE_IMAGE_CLASSES.button,
525
+ "aria-label": t.zoomOut,
526
+ disabled: pill.atMin,
527
+ onClick: () => zoom.controller.current?.step(-1),
528
+ children: /* @__PURE__ */ jsx4(MinusIcon, {})
529
+ }),
530
+ /* @__PURE__ */ jsxs3(DropdownMenu2, {
531
+ open: menuOpen,
532
+ onOpenChange: setMenuOpen,
533
+ children: [
534
+ /* @__PURE__ */ jsx4(DropdownMenuTrigger2, {
535
+ asChild: true,
536
+ children: /* @__PURE__ */ jsx4(Button2, {
537
+ type: "button",
538
+ variant: "ghost",
539
+ size: "sm",
540
+ className: ZOOMABLE_IMAGE_CLASSES.percent,
541
+ "aria-label": t.zoomLevel,
542
+ children: pill.percent
543
+ })
544
+ }),
545
+ /* @__PURE__ */ jsx4(DropdownMenuContent2, {
546
+ side: "top",
547
+ align: "center",
548
+ children: (menuOpen ? zoom.menuItems(t.zoomFit) : []).map((item) => /* @__PURE__ */ jsx4(DropdownMenuCheckboxItem2, {
549
+ checked: item.checked,
550
+ onSelect: () => {
551
+ const c = zoom.controller.current;
552
+ if (item.scale === null)
553
+ c?.rest();
554
+ else
555
+ c?.zoomScale(item.scale);
556
+ },
557
+ children: item.label
558
+ }, item.label))
559
+ })
560
+ ]
561
+ }),
562
+ /* @__PURE__ */ jsx4(Button2, {
563
+ type: "button",
564
+ variant: "ghost",
565
+ size: "icon-sm",
566
+ className: ZOOMABLE_IMAGE_CLASSES.button,
567
+ "aria-label": t.zoomIn,
568
+ disabled: pill.atMax,
569
+ onClick: () => zoom.controller.current?.step(1),
570
+ children: /* @__PURE__ */ jsx4(PlusIcon, {})
571
+ }),
572
+ /* @__PURE__ */ jsx4("span", {
573
+ "aria-hidden": "true",
574
+ "data-slot": "zoomable-image-divider",
575
+ className: ZOOMABLE_IMAGE_CLASSES.divider
576
+ }),
577
+ /* @__PURE__ */ jsx4(Button2, {
578
+ type: "button",
579
+ variant: "ghost",
580
+ size: "icon-sm",
581
+ className: ZOOMABLE_IMAGE_CLASSES.button,
582
+ "aria-label": t.zoomToFit,
583
+ "aria-pressed": pill.atRest,
584
+ onClick: () => zoom.controller.current?.rest(),
585
+ children: /* @__PURE__ */ jsx4(Maximize2Icon, {})
586
+ })
587
+ ]
588
+ }) : null
589
+ ]
590
+ });
591
+ }
592
+
593
+ export { cn2, getInitials2, ChoiceTilesFrame2, ChoiceTiles2, choiceColumns2, choiceToggle2, choiceValues2, buttonVariants2, Button2, DropdownMenu2, DropdownMenuPortal2, DropdownMenuTrigger2, DropdownMenuContent2, DropdownMenuGroup2, DropdownMenuItem2, DropdownMenuCheckboxItem2, DropdownMenuRadioGroup2, DropdownMenuRadioItem2, DropdownMenuLabel2, DropdownMenuSeparator2, DropdownMenuShortcut2, DropdownMenuSub2, DropdownMenuSubTrigger2, DropdownMenuSubContent2, useMediaQuery2, ZOOM_MATH2, useZoomableImage2, ZOOMABLE_IMAGE_STRINGS2, ZoomableImage2 };
594
+
595
+ //# debugId=D29E7D264854B4E764756E2164756E21
596
+ //# sourceMappingURL=index-eny7hh6w.js.map