@fumadocs/base-ui 16.7.13 → 16.7.15

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.
Files changed (16) hide show
  1. package/css/generated/shared.css +13 -3
  2. package/dist/components/toc/clerk.js +55 -46
  3. package/dist/components/toc/default.js +17 -7
  4. package/dist/components/ui/button.d.ts +1 -1
  5. package/dist/layouts/home/slots/header.d.ts +1 -1
  6. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Controlled.d.ts +36 -0
  7. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Controlled.js +435 -0
  8. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Uncontrolled.d.ts +7 -0
  9. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Uncontrolled.js +17 -0
  10. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/icons.js +24 -0
  11. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/index.d.ts +2 -0
  12. package/dist/node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/utils.js +372 -0
  13. package/dist/provider/base.d.ts +13 -12
  14. package/dist/provider/base.js +2 -2
  15. package/dist/style.css +8 -2
  16. package/package.json +8 -12
@@ -24,6 +24,8 @@
24
24
  @source inline("--easing");
25
25
  @source inline("--fd-animated-height");
26
26
  @source inline("--fd-banner-height");
27
+ @source inline("--offset-distance");
28
+ @source inline("--opacity");
27
29
  @source inline("--padding-right");
28
30
  @source inline("--popup-height");
29
31
  @source inline("--popup-width");
@@ -32,6 +34,8 @@
32
34
  @source inline("--shiki-light-bg");
33
35
  @source inline("--spacing");
34
36
  @source inline("--t");
37
+ @source inline("--track-bottom");
38
+ @source inline("--track-top");
35
39
  @source inline("--transform-origin");
36
40
  @source inline("-circle");
37
41
  @source inline("-mb-px");
@@ -65,6 +69,7 @@
65
69
  @source inline("[&_svg]:size-5");
66
70
  @source inline("[&_svg]:size-full");
67
71
  @source inline("[&_svg]:text-fd-muted-foreground");
72
+ @source inline("[offset-distance:var(--offset-distance,0)]");
68
73
  @source inline("[scrollbar-width:none]");
69
74
  @source inline("a");
70
75
  @source inline("about");
@@ -160,7 +165,9 @@
160
165
  @source inline("bottom-1.5");
161
166
  @source inline("bound");
162
167
  @source inline("boundary");
168
+ @source inline("box");
163
169
  @source inline("box-border");
170
+ @source inline("boxRef");
164
171
  @source inline("breaking");
165
172
  @source inline("button");
166
173
  @source inline("buttonVariants");
@@ -715,7 +722,6 @@
715
722
  @source inline("of");
716
723
  @source inline("official");
717
724
  @source inline("offset");
718
- @source inline("offsetDistance");
719
725
  @source inline("offsetPath");
720
726
  @source inline("offsetTop");
721
727
  @source inline("on");
@@ -739,8 +745,10 @@
739
745
  @source inline("onSelectCallback");
740
746
  @source inline("onTagChange");
741
747
  @source inline("onTagChangeCallback");
748
+ @source inline("onUpdate");
742
749
  @source inline("onValueChange");
743
750
  @source inline("only");
751
+ @source inline("opacity-(--opacity,0)");
744
752
  @source inline("opacity-0");
745
753
  @source inline("open");
746
754
  @source inline("opening");
@@ -853,6 +861,7 @@
853
861
  @source inline("rainbowColors");
854
862
  @source inline("raw");
855
863
  @source inline("rawTree");
864
+ @source inline("re-exported");
856
865
  @source inline("react");
857
866
  @source inline("react-dom");
858
867
  @source inline("react-hooks/exhaustive-deps");
@@ -909,7 +918,6 @@
909
918
  @source inline("rtl:rotate-180");
910
919
  @source inline("rtl:rotate-90");
911
920
  @source inline("s");
912
- @source inline("scale");
913
921
  @source inline("scope");
914
922
  @source inline("scroll");
915
923
  @source inline("scroll-into-view-if-needed");
@@ -1012,6 +1020,7 @@
1012
1020
  @source inline("supposed");
1013
1021
  @source inline("sure");
1014
1022
  @source inline("svg");
1023
+ @source inline("svgRef");
1015
1024
  @source inline("switch");
1016
1025
  @source inline("system");
1017
1026
  @source inline("t");
@@ -1067,6 +1076,7 @@
1067
1076
  @source inline("title");
1068
1077
  @source inline("to");
1069
1078
  @source inline("toc");
1079
+ @source inline("tocInfo");
1070
1080
  @source inline("tocNoHeadings");
1071
1081
  @source inline("toolbar");
1072
1082
  @source inline("top");
@@ -1083,7 +1093,7 @@
1083
1093
  @source inline("transition-[clip-path]");
1084
1094
  @source inline("transition-[height,opacity]");
1085
1095
  @source inline("transition-[height]");
1086
- @source inline("transition-[offset-distance]");
1096
+ @source inline("transition-[opacity,offset-distance]");
1087
1097
  @source inline("transition-[opacity,transform,translate]");
1088
1098
  @source inline("transition-[opacity,transform,width,height,scale,translate]");
1089
1099
  @source inline("transition-all");
@@ -105,13 +105,9 @@ function TOCItems({ ref, className, thumbBox = true, ...props }) {
105
105
  observer.unobserve(container);
106
106
  };
107
107
  }, [onPrint]);
108
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [svg && /* @__PURE__ */ jsxs("div", {
109
- className: "absolute top-0 inset-s-0",
110
- style: {
111
- width: svg.width,
112
- height: svg.height
113
- },
114
- children: [/* @__PURE__ */ jsx(ThumbTrack, { computed: svg }), thumbBox && /* @__PURE__ */ jsx(ThumbBox, { computed: svg })]
108
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [svg && /* @__PURE__ */ jsx(ThumbTrack, {
109
+ computed: svg,
110
+ thumbBox
115
111
  }), /* @__PURE__ */ jsx("div", {
116
112
  ref: mergeRefs(containerRef, ref),
117
113
  className: cn("flex flex-col", className),
@@ -125,48 +121,61 @@ function TOCEmpty() {
125
121
  children: text.tocNoHeadings
126
122
  });
127
123
  }
128
- function ThumbTrack({ computed }) {
129
- const items = Primitive.useItems();
130
- const startIdx = items.findIndex((item) => item.active);
131
- if (startIdx === -1) return;
132
- const endIdx = items.findLastIndex((item) => item.active);
133
- const top = `${computed.positions[startIdx][0]}px`;
134
- const bottom = `${computed.positions[endIdx][1]}px`;
135
- return /* @__PURE__ */ jsx("svg", {
136
- xmlns: "http://www.w3.org/2000/svg",
137
- viewBox: `0 0 ${computed.width} ${computed.height}`,
138
- className: "absolute transition-[clip-path]",
124
+ function ThumbTrack({ computed, thumbBox }) {
125
+ const svgRef = useRef(null);
126
+ const boxRef = useRef(null);
127
+ const previousRef = useRef(null);
128
+ const tocInfo = Primitive.useTOC();
129
+ const onUpdate = useCallback((items) => {
130
+ const svg = svgRef.current;
131
+ if (!svg) return;
132
+ const startIdx = items.findIndex((item) => item.active);
133
+ if (startIdx === -1) return;
134
+ const endIdx = items.findLastIndex((item) => item.active);
135
+ svg.style.setProperty("--track-top", `${computed.positions[startIdx][0]}px`);
136
+ svg.style.setProperty("--track-bottom", `${computed.positions[endIdx][1]}px`);
137
+ const box = boxRef.current;
138
+ if (box) {
139
+ let isUp = false;
140
+ if (previousRef.current) {
141
+ const prev = previousRef.current;
142
+ isUp = prev.startIdx > startIdx || prev.endIdx > endIdx || prev.startIdx === startIdx && prev.endIdx === endIdx && prev.isUp;
143
+ }
144
+ previousRef.current = {
145
+ startIdx,
146
+ endIdx,
147
+ isUp
148
+ };
149
+ box.style.setProperty("--offset-distance", isUp ? `${computed.itemLineLengths[startIdx][0]}px` : `${computed.itemLineLengths[endIdx][1]}px`);
150
+ box.style.setProperty("--opacity", items[isUp ? startIdx : endIdx].original._step !== void 0 ? "0" : "1");
151
+ }
152
+ }, [computed]);
153
+ Primitive.useTOCListener(onUpdate);
154
+ useEffect(() => {
155
+ onUpdate(tocInfo.get());
156
+ }, [onUpdate, tocInfo]);
157
+ return /* @__PURE__ */ jsxs("div", {
158
+ className: "absolute top-0 inset-s-0",
139
159
  style: {
140
160
  width: computed.width,
141
- height: computed.height,
142
- clipPath: `polygon(0 ${top}, 100% ${top}, 100% ${bottom}, 0 ${bottom})`
161
+ height: computed.height
143
162
  },
144
- children: computed.content
145
- });
146
- }
147
- function ThumbBox({ computed }) {
148
- const items = Primitive.useItems();
149
- const previousRef = useRef(null);
150
- const startIdx = items.findIndex((item) => item.active);
151
- if (startIdx === -1) return;
152
- const endIdx = items.findLastIndex((item) => item.active);
153
- let isUp = false;
154
- if (previousRef.current) {
155
- const prev = previousRef.current;
156
- isUp = prev.startIdx > startIdx || prev.endIdx > endIdx || prev.startIdx === startIdx && prev.endIdx === endIdx && prev.isUp;
157
- }
158
- previousRef.current = {
159
- startIdx,
160
- endIdx,
161
- isUp
162
- };
163
- return /* @__PURE__ */ jsx("div", {
164
- className: "absolute size-1 bg-fd-primary rounded-full transition-[offset-distance]",
165
- style: {
166
- offsetPath: `path("${computed.d}")`,
167
- offsetDistance: isUp ? computed.itemLineLengths[startIdx][0] : computed.itemLineLengths[endIdx][1],
168
- scale: items[isUp ? startIdx : endIdx].original._step !== void 0 ? "0" : "1"
169
- }
163
+ children: [/* @__PURE__ */ jsx("svg", {
164
+ ref: svgRef,
165
+ xmlns: "http://www.w3.org/2000/svg",
166
+ viewBox: `0 0 ${computed.width} ${computed.height}`,
167
+ className: "absolute transition-[clip-path]",
168
+ style: {
169
+ width: computed.width,
170
+ height: computed.height,
171
+ clipPath: `polygon(0 var(--track-top,0), 100% var(--track-top,0), 100% var(--track-bottom,0), 0 var(--track-bottom,0))`
172
+ },
173
+ children: computed.content
174
+ }), thumbBox && /* @__PURE__ */ jsx("div", {
175
+ ref: boxRef,
176
+ className: "absolute size-1 bg-fd-primary rounded-full [offset-distance:var(--offset-distance,0)] opacity-(--opacity,0) transition-[opacity,offset-distance]",
177
+ style: { offsetPath: `path("${computed.d}")` }
178
+ })]
170
179
  });
171
180
  }
172
181
  const a = 8;
@@ -53,15 +53,25 @@ function TOCItems({ ref, className, ...props }) {
53
53
  });
54
54
  }
55
55
  function TocThumb({ computed }) {
56
- const items = Primitive.useItems();
57
- const startIdx = items.findIndex((item) => item.active);
58
- if (startIdx === -1) return;
59
- const endIdx = items.findLastIndex((item) => item.active);
60
- const top = `${computed.positions[startIdx][0]}px`;
61
- const bottom = `${computed.positions[endIdx][1]}px`;
56
+ const ref = useRef(null);
57
+ const tocInfo = Primitive.useTOC();
58
+ const onUpdate = useCallback((items) => {
59
+ const element = ref.current;
60
+ if (!element) return;
61
+ const startIdx = items.findIndex((item) => item.active);
62
+ if (startIdx === -1) return;
63
+ const endIdx = items.findLastIndex((item) => item.active);
64
+ element.style.setProperty("--track-top", `${computed.positions[startIdx][0]}px`);
65
+ element.style.setProperty("--track-bottom", `${computed.positions[endIdx][1]}px`);
66
+ }, [computed]);
67
+ Primitive.useTOCListener(onUpdate);
68
+ useEffect(() => {
69
+ onUpdate(tocInfo.get());
70
+ }, [onUpdate, tocInfo]);
62
71
  return /* @__PURE__ */ jsx("div", {
72
+ ref,
63
73
  className: "absolute inset-y-0 inset-s-0 bg-fd-primary w-px transition-[clip-path]",
64
- style: { clipPath: `polygon(0 ${top}, 100% ${top}, 100% ${bottom}, 0 ${bottom})` }
74
+ style: { clipPath: `polygon(0 var(--track-top,0), 100% var(--track-top,0), 100% var(--track-bottom,0), 0 var(--track-bottom,0))` }
65
75
  });
66
76
  }
67
77
  function TOCEmpty() {
@@ -5,7 +5,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
5
5
  declare const buttonVariants: (props?: ({
6
6
  variant?: "primary" | "outline" | "ghost" | "secondary" | null | undefined;
7
7
  color?: "primary" | "outline" | "ghost" | "secondary" | null | undefined;
8
- size?: "sm" | "icon" | "icon-sm" | "icon-xs" | null | undefined;
8
+ size?: "icon" | "sm" | "icon-sm" | "icon-xs" | null | undefined;
9
9
  } & _$class_variance_authority_types0.ClassProp) | undefined) => string;
10
10
  type ButtonProps = VariantProps<typeof buttonVariants>;
11
11
  //#endregion
@@ -4,7 +4,7 @@ import * as _$class_variance_authority_types0 from "class-variance-authority/typ
4
4
 
5
5
  //#region src/layouts/home/slots/header.d.ts
6
6
  declare const navItemVariants: (props?: ({
7
- variant?: "icon" | "button" | "main" | null | undefined;
7
+ variant?: "icon" | "main" | "button" | null | undefined;
8
8
  } & _$class_variance_authority_types0.ClassProp) | undefined) => string;
9
9
  declare function Header(props: ComponentProps<'header'>): _$react_jsx_runtime0.JSX.Element;
10
10
  //#endregion
@@ -0,0 +1,36 @@
1
+ import React from "react";
2
+
3
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Controlled.d.ts
4
+ declare const enum ModalState {
5
+ LOADED = "LOADED",
6
+ LOADING = "LOADING",
7
+ UNLOADED = "UNLOADED",
8
+ UNLOADING = "UNLOADING"
9
+ }
10
+ interface ControlledProps {
11
+ a11yNameButtonUnzoom?: string;
12
+ a11yNameButtonZoom?: string;
13
+ canSwipeToUnzoom?: boolean;
14
+ children: React.ReactNode;
15
+ classDialog?: string;
16
+ IconUnzoom?: React.ElementType;
17
+ IconZoom?: React.ElementType;
18
+ isDisabled?: boolean;
19
+ isZoomed: boolean;
20
+ onZoomChange?: (value: boolean, data: {
21
+ event: React.SyntheticEvent | Event;
22
+ }) => void;
23
+ swipeToUnzoomThreshold?: number;
24
+ wrapElement?: 'div' | 'span';
25
+ ZoomContent?: (data: {
26
+ buttonUnzoom: React.ReactElement<HTMLButtonElement>;
27
+ img: React.ReactElement | null;
28
+ isZoomImgLoaded: boolean;
29
+ modalState: ModalState;
30
+ onUnzoom: (e: Event) => void;
31
+ }) => React.ReactElement;
32
+ zoomImg?: React.ImgHTMLAttributes<HTMLImageElement>;
33
+ zoomMargin?: number;
34
+ }
35
+ //#endregion
36
+ export { ControlledProps };
@@ -0,0 +1,435 @@
1
+ import { ICompress, IEnlarge } from "./icons.js";
2
+ import { adjustSvgIDs, getImgAlt, getImgSrc, getStyleGhost, getStyleModalImg, testDiv, testImg, testImgLoaded, testSvg } from "./utils.js";
3
+ import React from "react";
4
+ import ReactDOM from "react-dom";
5
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Controlled.js
6
+ const IMAGE_QUERY = [
7
+ "img",
8
+ "svg",
9
+ "[role=\"img\"]",
10
+ "[data-zoom]"
11
+ ].map((x) => `${x}:not([aria-hidden="true"])`).join(",");
12
+ const defaultBodyAttrs = {
13
+ overflow: "",
14
+ width: ""
15
+ };
16
+ function getDialogContainer() {
17
+ const existing = document.querySelector("[data-rmiz-portal]");
18
+ if (existing != null) return existing;
19
+ const el = document.createElement("div");
20
+ el.setAttribute("data-rmiz-portal", "");
21
+ document.body.appendChild(el);
22
+ return el;
23
+ }
24
+ function handleDialogCancelStatic(e) {
25
+ e.preventDefault();
26
+ }
27
+ function Controlled(props) {
28
+ return React.createElement(ControlledBase, { ...props });
29
+ }
30
+ var ControlledBase = class extends React.Component {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.state = {
34
+ id: "",
35
+ isZoomImgLoaded: false,
36
+ loadedImgEl: void 0,
37
+ modalState: "UNLOADED",
38
+ shouldRefresh: false,
39
+ styleGhost: {}
40
+ };
41
+ this.refContent = React.createRef();
42
+ this.refDialog = React.createRef();
43
+ this.refModalContent = React.createRef();
44
+ this.refModalImg = React.createRef();
45
+ this.refWrap = React.createRef();
46
+ this.imgEl = null;
47
+ this.isScaling = false;
48
+ this.prevBodyAttrs = defaultBodyAttrs;
49
+ this.styleModalImg = {};
50
+ this.handleModalStateChange = (prevModalState) => {
51
+ const { state: { modalState } } = this;
52
+ if (prevModalState !== "LOADING" && modalState === "LOADING") {
53
+ this.loadZoomImg();
54
+ window.addEventListener("resize", this.handleResize, { passive: true });
55
+ window.addEventListener("touchstart", this.handleTouchStart, { passive: true });
56
+ window.addEventListener("touchmove", this.handleTouchMove, { passive: true });
57
+ window.addEventListener("touchend", this.handleTouchEnd, { passive: true });
58
+ window.addEventListener("touchcancel", this.handleTouchCancel, { passive: true });
59
+ document.addEventListener("keydown", this.handleKeyDown, true);
60
+ } else if (prevModalState !== "LOADED" && modalState === "LOADED") window.addEventListener("wheel", this.handleWheel, { passive: true });
61
+ else if (prevModalState !== "UNLOADING" && modalState === "UNLOADING") {
62
+ this.ensureImgTransitionEnd();
63
+ window.removeEventListener("wheel", this.handleWheel);
64
+ window.removeEventListener("touchstart", this.handleTouchStart);
65
+ window.removeEventListener("touchmove", this.handleTouchMove);
66
+ window.removeEventListener("touchend", this.handleTouchEnd);
67
+ window.removeEventListener("touchcancel", this.handleTouchCancel);
68
+ document.removeEventListener("keydown", this.handleKeyDown, true);
69
+ } else if (prevModalState !== "UNLOADED" && modalState === "UNLOADED") {
70
+ this.bodyScrollEnable();
71
+ window.removeEventListener("resize", this.handleResize);
72
+ this.refModalImg.current?.removeEventListener("transitionend", this.handleImgTransitionEnd);
73
+ this.refDialog.current?.close();
74
+ }
75
+ };
76
+ this.setId = () => {
77
+ const gen4 = () => Math.random().toString(16).slice(-4);
78
+ this.setState({ id: gen4() + gen4() + gen4() });
79
+ };
80
+ this.setAndTrackImg = () => {
81
+ const { refContent: { current: contentEl } } = this;
82
+ if (contentEl == null) return;
83
+ this.imgEl = contentEl.querySelector(IMAGE_QUERY);
84
+ if (this.imgEl !== null) {
85
+ this.contentNotFoundChangeObserver?.disconnect();
86
+ this.imgEl.addEventListener("load", this.handleImgLoad);
87
+ this.imgEl.addEventListener("click", this.handleZoom);
88
+ if (this.state.loadedImgEl == null) this.handleImgLoad();
89
+ this.imgElResizeObserver = new ResizeObserver((entries) => {
90
+ const [entry] = entries;
91
+ if (entry?.target !== void 0) {
92
+ this.imgEl = entry.target;
93
+ this.setState({ styleGhost: getStyleGhost(this.imgEl) });
94
+ }
95
+ });
96
+ this.imgElResizeObserver.observe(this.imgEl);
97
+ if (this.contentChangeObserver == null) {
98
+ this.contentChangeObserver = new MutationObserver(() => {
99
+ this.setState({ styleGhost: getStyleGhost(this.imgEl) });
100
+ });
101
+ this.contentChangeObserver.observe(contentEl, {
102
+ attributes: true,
103
+ childList: true,
104
+ subtree: true
105
+ });
106
+ }
107
+ } else if (this.contentNotFoundChangeObserver == null) {
108
+ this.contentNotFoundChangeObserver = new MutationObserver(this.setAndTrackImg);
109
+ this.contentNotFoundChangeObserver.observe(contentEl, {
110
+ childList: true,
111
+ subtree: true
112
+ });
113
+ }
114
+ };
115
+ this.handleIfZoomChanged = (prevIsZoomed) => {
116
+ const { props: { isZoomed } } = this;
117
+ if (!prevIsZoomed && isZoomed) this.zoom();
118
+ else if (prevIsZoomed && !isZoomed) this.unzoom();
119
+ };
120
+ this.handleImgLoad = () => {
121
+ const imgSrc = getImgSrc(this.imgEl);
122
+ if (imgSrc == null || imgSrc === "") return;
123
+ const img = new Image();
124
+ const { imgEl } = this;
125
+ if (testImg(imgEl)) {
126
+ const { sizes, srcset, crossOrigin } = imgEl;
127
+ img.sizes = sizes;
128
+ img.srcset = srcset;
129
+ img.crossOrigin = crossOrigin;
130
+ }
131
+ img.src = imgSrc;
132
+ const setLoaded = () => {
133
+ this.setState({
134
+ loadedImgEl: img,
135
+ styleGhost: getStyleGhost(this.imgEl)
136
+ });
137
+ };
138
+ img.decode().then(setLoaded).catch(() => {
139
+ if (testImgLoaded(img)) {
140
+ setLoaded();
141
+ return;
142
+ }
143
+ img.onload = setLoaded;
144
+ });
145
+ };
146
+ this.handleZoom = (e) => {
147
+ if (this.props.isDisabled !== true && this.hasImage()) this.props.onZoomChange?.(true, { event: e });
148
+ };
149
+ this.handleUnzoom = (e) => {
150
+ if (this.props.isDisabled !== true) this.props.onZoomChange?.(false, { event: e });
151
+ };
152
+ this.handleBtnUnzoomClick = (e) => {
153
+ e.preventDefault();
154
+ e.stopPropagation();
155
+ this.handleUnzoom(e);
156
+ };
157
+ this.handleDialogClick = (e) => {
158
+ if (e.target === this.refModalContent.current || e.target === this.refModalImg.current) {
159
+ e.stopPropagation();
160
+ this.handleUnzoom(e);
161
+ }
162
+ };
163
+ this.handleDialogClose = (e) => {
164
+ e.stopPropagation();
165
+ this.handleUnzoom(e);
166
+ };
167
+ this.handleKeyDown = (e) => {
168
+ if (e.key === "Escape") {
169
+ e.preventDefault();
170
+ e.stopPropagation();
171
+ this.handleUnzoom(e);
172
+ }
173
+ };
174
+ this.handleWheel = (e) => {
175
+ if (e.ctrlKey) return;
176
+ e.stopPropagation();
177
+ queueMicrotask(() => {
178
+ this.handleUnzoom(e);
179
+ });
180
+ };
181
+ this.handleTouchStart = (e) => {
182
+ if (e.touches.length > 1) {
183
+ this.isScaling = true;
184
+ return;
185
+ }
186
+ const { changedTouches } = e;
187
+ const [changedTouch] = changedTouches;
188
+ if (changedTouches.length === 1 && changedTouch !== void 0) {
189
+ const { screenY } = changedTouch;
190
+ this.touchYStart = screenY;
191
+ }
192
+ };
193
+ this.handleTouchMove = (e) => {
194
+ const browserScale = window.visualViewport?.scale ?? 1;
195
+ const { changedTouches: changedTouchesMove } = e;
196
+ const [changedTouchMove] = changedTouchesMove;
197
+ if (this.props.canSwipeToUnzoom && !this.isScaling && browserScale <= 1 && this.touchYStart != null && changedTouchMove !== void 0) {
198
+ const { screenY } = changedTouchMove;
199
+ this.touchYEnd = screenY;
200
+ const max = Math.max(this.touchYStart, this.touchYEnd);
201
+ const min = Math.min(this.touchYStart, this.touchYEnd);
202
+ if (Math.abs(max - min) > this.props.swipeToUnzoomThreshold) {
203
+ this.touchYStart = void 0;
204
+ this.touchYEnd = void 0;
205
+ this.handleUnzoom(e);
206
+ }
207
+ }
208
+ };
209
+ this.handleTouchEnd = () => {
210
+ this.isScaling = false;
211
+ this.touchYStart = void 0;
212
+ this.touchYEnd = void 0;
213
+ };
214
+ this.handleTouchCancel = () => {
215
+ this.isScaling = false;
216
+ this.touchYStart = void 0;
217
+ this.touchYEnd = void 0;
218
+ };
219
+ this.handleResize = () => {
220
+ this.setState({ shouldRefresh: true });
221
+ };
222
+ this.hasImage = () => this.imgEl !== null && (this.state.loadedImgEl !== void 0 || testSvg(this.imgEl)) && window.getComputedStyle(this.imgEl).display !== "none";
223
+ this.zoom = () => {
224
+ this.bodyScrollDisable();
225
+ this.refDialog.current?.showModal();
226
+ this.refModalImg.current?.addEventListener("transitionend", this.handleImgTransitionEnd);
227
+ this.setState({ modalState: "LOADING" });
228
+ };
229
+ this.unzoom = () => {
230
+ this.setState({ modalState: "UNLOADING" });
231
+ };
232
+ this.handleImgTransitionEnd = () => {
233
+ clearTimeout(this.timeoutTransitionEnd);
234
+ if (this.state.modalState === "LOADING") this.setState({ modalState: "LOADED" });
235
+ else if (this.state.modalState === "UNLOADING") this.setState({
236
+ shouldRefresh: false,
237
+ modalState: "UNLOADED"
238
+ });
239
+ };
240
+ this.ensureImgTransitionEnd = () => {
241
+ if (this.refModalImg.current !== null) {
242
+ const { transitionDuration: td } = window.getComputedStyle(this.refModalImg.current);
243
+ const tdFloat = parseFloat(td);
244
+ if (tdFloat !== 0 && !Number.isNaN(tdFloat)) {
245
+ const tdMs = tdFloat * (td.endsWith("ms") ? 1 : 1e3) + 50;
246
+ this.timeoutTransitionEnd = setTimeout(this.handleImgTransitionEnd, tdMs);
247
+ }
248
+ }
249
+ };
250
+ this.bodyScrollDisable = () => {
251
+ this.prevBodyAttrs = {
252
+ overflow: document.body.style.overflow,
253
+ width: document.body.style.width
254
+ };
255
+ const { body: { clientWidth } } = document;
256
+ document.body.style.overflow = "hidden";
257
+ document.body.style.width = `${clientWidth}px`;
258
+ };
259
+ this.bodyScrollEnable = () => {
260
+ const { prevBodyAttrs: { overflow, width } } = this;
261
+ document.body.style.width = width;
262
+ document.body.style.overflow = overflow;
263
+ this.prevBodyAttrs = defaultBodyAttrs;
264
+ };
265
+ this.loadZoomImg = () => {
266
+ const { props: { zoomImg } } = this;
267
+ if (zoomImg == null) return;
268
+ const { src: zoomImgSrc } = zoomImg;
269
+ if (zoomImgSrc !== void 0 && zoomImgSrc !== "") {
270
+ const img = new Image();
271
+ img.sizes = zoomImg.sizes ?? "";
272
+ img.srcset = zoomImg.srcSet ?? "";
273
+ img.crossOrigin = zoomImg.crossOrigin ?? void 0;
274
+ img.src = zoomImgSrc;
275
+ const setLoaded = () => {
276
+ this.setState({ isZoomImgLoaded: true });
277
+ };
278
+ img.decode().then(setLoaded).catch(() => {
279
+ if (testImgLoaded(img)) {
280
+ setLoaded();
281
+ return;
282
+ }
283
+ img.onload = setLoaded;
284
+ });
285
+ }
286
+ };
287
+ this.UNSAFE_handleSvg = () => {
288
+ const { imgEl, refModalImg, styleModalImg } = this;
289
+ if (testSvg(imgEl)) {
290
+ const svgEl = imgEl.cloneNode(true);
291
+ adjustSvgIDs(svgEl);
292
+ svgEl.style.width = `${styleModalImg.width ?? 0}px`;
293
+ svgEl.style.height = `${styleModalImg.height ?? 0}px`;
294
+ svgEl.addEventListener("click", this.handleUnzoom);
295
+ refModalImg.current?.firstChild?.remove();
296
+ refModalImg.current?.appendChild(svgEl);
297
+ }
298
+ };
299
+ }
300
+ render() {
301
+ const { handleBtnUnzoomClick, handleDialogClick, handleDialogClose, handleUnzoom, handleZoom, imgEl, props: { a11yNameButtonUnzoom, a11yNameButtonZoom, children, classDialog, IconUnzoom, IconZoom, isZoomed, wrapElement: WrapElement, ZoomContent, zoomImg, zoomMargin }, refContent, refDialog, refModalContent, refModalImg, refWrap, state: { id, isZoomImgLoaded, loadedImgEl, modalState, shouldRefresh, styleGhost } } = this;
302
+ const idModal = `rmiz-modal-${id}`;
303
+ const idModalImg = `rmiz-modal-img-${id}`;
304
+ const isDiv = testDiv(imgEl);
305
+ const isImg = testImg(imgEl);
306
+ const isSvg = testSvg(imgEl);
307
+ const imgAlt = getImgAlt(imgEl);
308
+ const imgSrc = getImgSrc(imgEl);
309
+ const imgSizes = isImg ? imgEl.sizes : void 0;
310
+ const imgSrcSet = isImg ? imgEl.srcset : void 0;
311
+ const imgCrossOrigin = isImg ? imgEl.crossOrigin : void 0;
312
+ const hasZoomImg = zoomImg?.src !== void 0 && zoomImg.src !== "";
313
+ const hasImage = this.hasImage();
314
+ const labelBtnZoom = imgAlt !== void 0 && imgAlt !== "" ? `${a11yNameButtonZoom}: ${imgAlt}` : a11yNameButtonZoom;
315
+ const isModalActive = modalState === "LOADING" || modalState === "LOADED";
316
+ const dataContentState = hasImage ? "found" : "not-found";
317
+ const dataOverlayState = modalState === "UNLOADED" || modalState === "UNLOADING" ? "hidden" : "visible";
318
+ const styleContent = { visibility: modalState === "UNLOADED" ? "visible" : "hidden" };
319
+ this.styleModalImg = hasImage && imgEl !== null ? getStyleModalImg({
320
+ hasZoomImg,
321
+ imgSrc,
322
+ isSvg,
323
+ isZoomed: isZoomed && isModalActive,
324
+ loadedImgEl,
325
+ offset: zoomMargin,
326
+ shouldRefresh,
327
+ targetEl: imgEl
328
+ }) : {};
329
+ let modalContent = null;
330
+ if (hasImage) {
331
+ const modalImg = isImg || isDiv ? React.createElement("img", {
332
+ alt: imgAlt,
333
+ crossOrigin: imgCrossOrigin,
334
+ sizes: imgSizes,
335
+ src: imgSrc,
336
+ srcSet: imgSrcSet,
337
+ ...isZoomImgLoaded && modalState === "LOADED" ? zoomImg : {},
338
+ "data-rmiz-modal-img": "",
339
+ height: this.styleModalImg.height ?? void 0,
340
+ id: idModalImg,
341
+ ref: refModalImg,
342
+ style: this.styleModalImg,
343
+ width: this.styleModalImg.width ?? void 0
344
+ }) : isSvg ? React.createElement("div", {
345
+ "data-rmiz-modal-img": true,
346
+ ref: refModalImg,
347
+ style: this.styleModalImg
348
+ }) : null;
349
+ const modalBtnUnzoom = React.createElement("button", {
350
+ "aria-label": a11yNameButtonUnzoom,
351
+ "data-rmiz-btn-unzoom": "",
352
+ onClick: handleBtnUnzoomClick,
353
+ type: "button"
354
+ }, React.createElement(IconUnzoom, null));
355
+ modalContent = ZoomContent == null ? React.createElement(React.Fragment, null, modalImg, modalBtnUnzoom) : React.createElement(ZoomContent, {
356
+ buttonUnzoom: modalBtnUnzoom,
357
+ modalState,
358
+ img: modalImg,
359
+ isZoomImgLoaded,
360
+ onUnzoom: handleUnzoom
361
+ });
362
+ }
363
+ return React.createElement(WrapElement, {
364
+ "aria-owns": idModal,
365
+ "data-rmiz": "",
366
+ ref: refWrap
367
+ }, React.createElement(WrapElement, {
368
+ "data-rmiz-content": dataContentState,
369
+ ref: refContent,
370
+ style: styleContent
371
+ }, children), hasImage && React.createElement(WrapElement, {
372
+ "data-rmiz-ghost": "",
373
+ style: styleGhost
374
+ }, React.createElement("button", {
375
+ "aria-label": labelBtnZoom,
376
+ "data-rmiz-btn-zoom": "",
377
+ onClick: handleZoom,
378
+ type: "button"
379
+ }, React.createElement(IconZoom, null))), hasImage && ReactDOM.createPortal(React.createElement("dialog", {
380
+ "aria-labelledby": idModalImg,
381
+ "aria-modal": "true",
382
+ className: classDialog,
383
+ "data-rmiz-modal": "",
384
+ id: idModal,
385
+ onClick: handleDialogClick,
386
+ onClose: handleDialogClose,
387
+ onCancel: handleDialogCancelStatic,
388
+ ref: refDialog,
389
+ role: "dialog"
390
+ }, React.createElement("div", { "data-rmiz-modal-overlay": dataOverlayState }), React.createElement("div", {
391
+ "data-rmiz-modal-content": "",
392
+ ref: refModalContent
393
+ }, modalContent)), getDialogContainer()));
394
+ }
395
+ componentDidMount() {
396
+ this.setId();
397
+ this.setAndTrackImg();
398
+ this.handleImgLoad();
399
+ this.UNSAFE_handleSvg();
400
+ }
401
+ componentWillUnmount() {
402
+ if (this.state.modalState !== "UNLOADED") this.bodyScrollEnable();
403
+ this.contentChangeObserver?.disconnect();
404
+ this.contentNotFoundChangeObserver?.disconnect();
405
+ this.imgElResizeObserver?.disconnect();
406
+ this.imgEl?.removeEventListener("load", this.handleImgLoad);
407
+ this.imgEl?.removeEventListener("click", this.handleZoom);
408
+ this.refModalImg.current?.removeEventListener("transitionend", this.handleImgTransitionEnd);
409
+ window.removeEventListener("wheel", this.handleWheel);
410
+ window.removeEventListener("touchstart", this.handleTouchStart);
411
+ window.removeEventListener("touchmove", this.handleTouchMove);
412
+ window.removeEventListener("touchend", this.handleTouchEnd);
413
+ window.removeEventListener("touchcancel", this.handleTouchCancel);
414
+ window.removeEventListener("resize", this.handleResize);
415
+ document.removeEventListener("keydown", this.handleKeyDown, true);
416
+ }
417
+ componentDidUpdate(prevProps, prevState) {
418
+ this.handleModalStateChange(prevState.modalState);
419
+ this.UNSAFE_handleSvg();
420
+ this.handleIfZoomChanged(prevProps.isZoomed);
421
+ }
422
+ };
423
+ ControlledBase.defaultProps = {
424
+ a11yNameButtonUnzoom: "Minimize image",
425
+ a11yNameButtonZoom: "Expand image",
426
+ canSwipeToUnzoom: true,
427
+ IconUnzoom: ICompress,
428
+ IconZoom: IEnlarge,
429
+ isDisabled: false,
430
+ swipeToUnzoomThreshold: 10,
431
+ wrapElement: "div",
432
+ zoomMargin: 0
433
+ };
434
+ //#endregion
435
+ export { Controlled };
@@ -0,0 +1,7 @@
1
+ import { ControlledProps } from "./Controlled.js";
2
+ import React from "react";
3
+
4
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Uncontrolled.d.ts
5
+ type UncontrolledProps = Omit<ControlledProps, 'isZoomed'>;
6
+ //#endregion
7
+ export { UncontrolledProps };
@@ -0,0 +1,17 @@
1
+ import { Controlled } from "./Controlled.js";
2
+ import React from "react";
3
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/Uncontrolled.js
4
+ function Uncontrolled({ onZoomChange, ...props }) {
5
+ const [isZoomed, setIsZoomed] = React.useState(false);
6
+ const handleZoomChange = React.useCallback((value, { event }) => {
7
+ setIsZoomed(value);
8
+ onZoomChange?.(value, { event });
9
+ }, [onZoomChange]);
10
+ return React.createElement(Controlled, {
11
+ ...props,
12
+ isZoomed,
13
+ onZoomChange: handleZoomChange
14
+ });
15
+ }
16
+ //#endregion
17
+ export { Uncontrolled };
@@ -0,0 +1,24 @@
1
+ import React from "react";
2
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/icons.js
3
+ function ICompress() {
4
+ return React.createElement("svg", {
5
+ "aria-hidden": "true",
6
+ "data-rmiz-btn-unzoom-icon": true,
7
+ fill: "currentColor",
8
+ focusable: "false",
9
+ viewBox: "0 0 16 16",
10
+ xmlns: "http://www.w3.org/2000/svg"
11
+ }, React.createElement("path", { d: "M 14.144531 1.148438 L 9 6.292969 L 9 3 L 8 3 L 8 8 L 13 8 L 13 7 L 9.707031 7 L 14.855469 1.851563 Z M 8 8 L 3 8 L 3 9 L 6.292969 9 L 1.148438 14.144531 L 1.851563 14.855469 L 7 9.707031 L 7 13 L 8 13 Z" }));
12
+ }
13
+ function IEnlarge() {
14
+ return React.createElement("svg", {
15
+ "aria-hidden": "true",
16
+ "data-rmiz-btn-zoom-icon": true,
17
+ fill: "currentColor",
18
+ focusable: "false",
19
+ viewBox: "0 0 16 16",
20
+ xmlns: "http://www.w3.org/2000/svg"
21
+ }, React.createElement("path", { d: "M 9 1 L 9 2 L 12.292969 2 L 2 12.292969 L 2 9 L 1 9 L 1 14 L 6 14 L 6 13 L 2.707031 13 L 13 2.707031 L 13 6 L 14 6 L 14 1 Z" }));
22
+ }
23
+ //#endregion
24
+ export { ICompress, IEnlarge };
@@ -0,0 +1,2 @@
1
+ import { ControlledProps } from "./Controlled.js";
2
+ import { UncontrolledProps } from "./Uncontrolled.js";
@@ -0,0 +1,372 @@
1
+ //#region ../../node_modules/.pnpm/react-medium-image-zoom@5.4.3_react-dom@19.2.5_react@19.2.5__react@19.2.5/node_modules/react-medium-image-zoom/dist/utils.js
2
+ function isElement(el) {
3
+ if (typeof Element === "undefined") return false;
4
+ return el instanceof Element;
5
+ }
6
+ const testElType = (type, el) => isElement(el) && el.tagName.toUpperCase() === type;
7
+ const testDiv = (el) => testElType("DIV", el) || testElType("SPAN", el);
8
+ const testImg = (el) => testElType("IMG", el);
9
+ const testImgLoaded = (el) => el.complete && el.naturalHeight !== 0;
10
+ const testSvg = (el) => testElType("SVG", el);
11
+ const getScaleToWindow = ({ height, offset, width }) => Math.min((window.innerWidth - offset * 2) / width, (window.innerHeight - offset * 2) / height);
12
+ const getScaleToWindowMax = ({ containerHeight, containerWidth, offset, targetHeight, targetWidth }) => {
13
+ const scale = getScaleToWindow({
14
+ height: targetHeight,
15
+ offset,
16
+ width: targetWidth
17
+ });
18
+ const ratio = targetWidth > targetHeight ? targetWidth / containerWidth : targetHeight / containerHeight;
19
+ return scale > 1 ? ratio : scale * ratio;
20
+ };
21
+ const getScale = ({ containerHeight, containerWidth, hasScalableSrc, offset, targetHeight, targetWidth }) => {
22
+ if (containerHeight === 0 || containerWidth === 0) return 1;
23
+ return !hasScalableSrc && targetHeight !== 0 && targetWidth !== 0 ? getScaleToWindowMax({
24
+ containerHeight,
25
+ containerWidth,
26
+ offset,
27
+ targetHeight,
28
+ targetWidth
29
+ }) : getScaleToWindow({
30
+ height: containerHeight,
31
+ offset,
32
+ width: containerWidth
33
+ });
34
+ };
35
+ const URL_REGEX = /url(?:\(['"]?)(?<url>.*?)(?:['"]?\))/;
36
+ const getImgSrc = (imgEl) => {
37
+ if (imgEl !== null) {
38
+ if (testImg(imgEl)) return imgEl.currentSrc === "" ? void 0 : imgEl.currentSrc;
39
+ else if (testDiv(imgEl)) {
40
+ const { backgroundImage: bgImg } = window.getComputedStyle(imgEl);
41
+ if (bgImg !== "") return URL_REGEX.exec(bgImg)?.[1];
42
+ }
43
+ }
44
+ };
45
+ const getImgAlt = (imgEl) => {
46
+ if (imgEl !== null) if (testImg(imgEl)) return imgEl.alt;
47
+ else return imgEl.getAttribute("aria-label") ?? void 0;
48
+ };
49
+ const getImgRegularStyle = ({ containerHeight, containerLeft, containerTop, containerWidth, hasScalableSrc, offset, targetHeight, targetWidth }) => {
50
+ const scale = getScale({
51
+ containerHeight,
52
+ containerWidth,
53
+ hasScalableSrc,
54
+ offset,
55
+ targetHeight,
56
+ targetWidth
57
+ });
58
+ return {
59
+ top: containerTop,
60
+ left: containerLeft,
61
+ width: containerWidth * scale,
62
+ height: containerHeight * scale,
63
+ transform: `translate(0,0) scale(${1 / scale})`
64
+ };
65
+ };
66
+ const parsePosition = ({ position, relativeNum }) => {
67
+ const positionNum = parseFloat(position);
68
+ return position.endsWith("%") ? relativeNum * positionNum / 100 : positionNum;
69
+ };
70
+ const getImgObjectFitStyle = ({ containerHeight, containerLeft, containerTop, containerWidth, hasScalableSrc, objectFit: objectFitParam, objectPosition, offset, targetHeight, targetWidth }) => {
71
+ let resolvedObjectFit = objectFitParam;
72
+ if (resolvedObjectFit === "scale-down") if (targetWidth <= containerWidth && targetHeight <= containerHeight) resolvedObjectFit = "none";
73
+ else resolvedObjectFit = "contain";
74
+ if (resolvedObjectFit === "cover" || resolvedObjectFit === "contain") {
75
+ const widthRatio = containerWidth / targetWidth;
76
+ const heightRatio = containerHeight / targetHeight;
77
+ const ratio = resolvedObjectFit === "cover" ? Math.max(widthRatio, heightRatio) : Math.min(widthRatio, heightRatio);
78
+ const [posLeft = "50%", posTop = "50%"] = objectPosition.split(" ");
79
+ const posX = parsePosition({
80
+ position: posLeft,
81
+ relativeNum: containerWidth - targetWidth * ratio
82
+ });
83
+ const posY = parsePosition({
84
+ position: posTop,
85
+ relativeNum: containerHeight - targetHeight * ratio
86
+ });
87
+ const scale = getScale({
88
+ containerHeight: targetHeight * ratio,
89
+ containerWidth: targetWidth * ratio,
90
+ hasScalableSrc,
91
+ offset,
92
+ targetHeight,
93
+ targetWidth
94
+ });
95
+ return {
96
+ top: containerTop + posY,
97
+ left: containerLeft + posX,
98
+ width: targetWidth * ratio * scale,
99
+ height: targetHeight * ratio * scale,
100
+ transform: `translate(0,0) scale(${1 / scale})`
101
+ };
102
+ } else if (resolvedObjectFit === "none") {
103
+ const [posLeft = "50%", posTop = "50%"] = objectPosition.split(" ");
104
+ const posX = parsePosition({
105
+ position: posLeft,
106
+ relativeNum: containerWidth - targetWidth
107
+ });
108
+ const posY = parsePosition({
109
+ position: posTop,
110
+ relativeNum: containerHeight - targetHeight
111
+ });
112
+ const scale = getScale({
113
+ containerHeight: targetHeight,
114
+ containerWidth: targetWidth,
115
+ hasScalableSrc,
116
+ offset,
117
+ targetHeight,
118
+ targetWidth
119
+ });
120
+ return {
121
+ top: containerTop + posY,
122
+ left: containerLeft + posX,
123
+ width: targetWidth * scale,
124
+ height: targetHeight * scale,
125
+ transform: `translate(0,0) scale(${1 / scale})`
126
+ };
127
+ } else if (resolvedObjectFit === "fill") {
128
+ const widthRatio = containerWidth / targetWidth;
129
+ const heightRatio = containerHeight / targetHeight;
130
+ const ratio = Math.max(widthRatio, heightRatio);
131
+ const scale = getScale({
132
+ containerHeight: targetHeight * ratio,
133
+ containerWidth: targetWidth * ratio,
134
+ hasScalableSrc,
135
+ offset,
136
+ targetHeight,
137
+ targetWidth
138
+ });
139
+ return {
140
+ width: containerWidth * scale,
141
+ height: containerHeight * scale,
142
+ transform: `translate(0,0) scale(${1 / scale})`
143
+ };
144
+ } else return {};
145
+ };
146
+ const getDivImgStyle = ({ backgroundPosition, backgroundSize, containerHeight, containerLeft, containerTop, containerWidth, hasScalableSrc, offset, targetHeight, targetWidth }) => {
147
+ if (backgroundSize === "cover" || backgroundSize === "contain") {
148
+ const widthRatio = containerWidth / targetWidth;
149
+ const heightRatio = containerHeight / targetHeight;
150
+ const ratio = backgroundSize === "cover" ? Math.max(widthRatio, heightRatio) : Math.min(widthRatio, heightRatio);
151
+ const [posLeft = "50%", posTop = "50%"] = backgroundPosition.split(" ");
152
+ const posX = parsePosition({
153
+ position: posLeft,
154
+ relativeNum: containerWidth - targetWidth * ratio
155
+ });
156
+ const posY = parsePosition({
157
+ position: posTop,
158
+ relativeNum: containerHeight - targetHeight * ratio
159
+ });
160
+ const scale = getScale({
161
+ containerHeight: targetHeight * ratio,
162
+ containerWidth: targetWidth * ratio,
163
+ hasScalableSrc,
164
+ offset,
165
+ targetHeight,
166
+ targetWidth
167
+ });
168
+ return {
169
+ top: containerTop + posY,
170
+ left: containerLeft + posX,
171
+ width: targetWidth * ratio * scale,
172
+ height: targetHeight * ratio * scale,
173
+ transform: `translate(0,0) scale(${1 / scale})`
174
+ };
175
+ } else if (backgroundSize === "auto") {
176
+ const [posLeft = "50%", posTop = "50%"] = backgroundPosition.split(" ");
177
+ const posX = parsePosition({
178
+ position: posLeft,
179
+ relativeNum: containerWidth - targetWidth
180
+ });
181
+ const posY = parsePosition({
182
+ position: posTop,
183
+ relativeNum: containerHeight - targetHeight
184
+ });
185
+ const scale = getScale({
186
+ containerHeight: targetHeight,
187
+ containerWidth: targetWidth,
188
+ hasScalableSrc,
189
+ offset,
190
+ targetHeight,
191
+ targetWidth
192
+ });
193
+ return {
194
+ top: containerTop + posY,
195
+ left: containerLeft + posX,
196
+ width: targetWidth * scale,
197
+ height: targetHeight * scale,
198
+ transform: `translate(0,0) scale(${1 / scale})`
199
+ };
200
+ } else {
201
+ const [sizeW = "50%", sizeH = "50%"] = backgroundSize.split(" ");
202
+ const sizeWidth = parsePosition({
203
+ position: sizeW,
204
+ relativeNum: containerWidth
205
+ });
206
+ const sizeHeight = parsePosition({
207
+ position: sizeH,
208
+ relativeNum: containerHeight
209
+ });
210
+ const widthRatio = sizeWidth / targetWidth;
211
+ const heightRatio = sizeHeight / targetHeight;
212
+ const ratio = Math.min(widthRatio, heightRatio);
213
+ const [posLeft = "50%", posTop = "50%"] = backgroundPosition.split(" ");
214
+ const posX = parsePosition({
215
+ position: posLeft,
216
+ relativeNum: containerWidth - targetWidth * ratio
217
+ });
218
+ const posY = parsePosition({
219
+ position: posTop,
220
+ relativeNum: containerHeight - targetHeight * ratio
221
+ });
222
+ const scale = getScale({
223
+ containerHeight: targetHeight * ratio,
224
+ containerWidth: targetWidth * ratio,
225
+ hasScalableSrc,
226
+ offset,
227
+ targetHeight,
228
+ targetWidth
229
+ });
230
+ return {
231
+ top: containerTop + posY,
232
+ left: containerLeft + posX,
233
+ width: targetWidth * ratio * scale,
234
+ height: targetHeight * ratio * scale,
235
+ transform: `translate(0,0) scale(${1 / scale})`
236
+ };
237
+ }
238
+ };
239
+ const SRC_SVG_REGEX = /\.svg$/i;
240
+ const getStyleModalImg = ({ hasZoomImg, imgSrc, isSvg, isZoomed, loadedImgEl, offset, shouldRefresh, targetEl }) => {
241
+ const hasScalableSrc = isSvg || imgSrc?.slice(0, 18) === "data:image/svg+xml" || hasZoomImg || imgSrc !== void 0 && SRC_SVG_REGEX.test(imgSrc);
242
+ const imgRect = targetEl.getBoundingClientRect();
243
+ const targetElComputedStyle = window.getComputedStyle(targetEl);
244
+ const isDivImg = loadedImgEl != null && testDiv(targetEl);
245
+ const isImgObjectFit = loadedImgEl != null && !isDivImg;
246
+ const targetHeight = loadedImgEl != null && loadedImgEl.naturalHeight !== 0 ? loadedImgEl.naturalHeight : imgRect.height;
247
+ const targetWidth = loadedImgEl != null && loadedImgEl.naturalWidth !== 0 ? loadedImgEl.naturalWidth : imgRect.width;
248
+ const styleImgRegular = getImgRegularStyle({
249
+ containerHeight: imgRect.height,
250
+ containerLeft: imgRect.left,
251
+ containerTop: imgRect.top,
252
+ containerWidth: imgRect.width,
253
+ hasScalableSrc,
254
+ offset,
255
+ targetHeight,
256
+ targetWidth
257
+ });
258
+ const styleImgObjectFit = isImgObjectFit ? getImgObjectFitStyle({
259
+ containerHeight: imgRect.height,
260
+ containerLeft: imgRect.left,
261
+ containerTop: imgRect.top,
262
+ containerWidth: imgRect.width,
263
+ hasScalableSrc,
264
+ objectFit: targetElComputedStyle.objectFit,
265
+ objectPosition: targetElComputedStyle.objectPosition,
266
+ offset,
267
+ targetHeight,
268
+ targetWidth
269
+ }) : void 0;
270
+ const styleDivImg = isDivImg ? getDivImgStyle({
271
+ backgroundPosition: targetElComputedStyle.backgroundPosition,
272
+ backgroundSize: targetElComputedStyle.backgroundSize,
273
+ containerHeight: imgRect.height,
274
+ containerLeft: imgRect.left,
275
+ containerTop: imgRect.top,
276
+ containerWidth: imgRect.width,
277
+ hasScalableSrc,
278
+ offset,
279
+ targetHeight,
280
+ targetWidth
281
+ }) : void 0;
282
+ const style = {
283
+ ...styleImgRegular,
284
+ ...styleImgObjectFit,
285
+ ...styleDivImg
286
+ };
287
+ if (isZoomed) {
288
+ const viewportX = window.innerWidth / 2;
289
+ const viewportY = window.innerHeight / 2;
290
+ const childCenterX = parseFloat(String(style.left ?? 0)) + parseFloat(String(style.width ?? 0)) / 2;
291
+ const childCenterY = parseFloat(String(style.top ?? 0)) + parseFloat(String(style.height ?? 0)) / 2;
292
+ const translateX = viewportX - childCenterX;
293
+ const translateY = viewportY - childCenterY;
294
+ if (shouldRefresh) style.transitionDuration = "0.01ms";
295
+ style.transform = `translate(${translateX}px,${translateY}px) scale(1)`;
296
+ }
297
+ return style;
298
+ };
299
+ const getStyleGhost = (imgEl) => {
300
+ if (imgEl == null) return {};
301
+ if (testSvg(imgEl)) {
302
+ const { parentElement: parentEl } = imgEl;
303
+ const rect = imgEl.getBoundingClientRect();
304
+ if (parentEl == null) return {
305
+ height: rect.height,
306
+ left: rect.left,
307
+ width: rect.width,
308
+ top: rect.top
309
+ };
310
+ else {
311
+ const parentRect = parentEl.getBoundingClientRect();
312
+ return {
313
+ height: rect.height,
314
+ left: parentRect.left - rect.left,
315
+ top: parentRect.top - rect.top,
316
+ width: rect.width
317
+ };
318
+ }
319
+ } else return {
320
+ height: imgEl.offsetHeight,
321
+ left: imgEl.offsetLeft,
322
+ width: imgEl.offsetWidth,
323
+ top: imgEl.offsetTop
324
+ };
325
+ };
326
+ const adjustSvgIDs = (svgEl) => {
327
+ const newIdSuffix = "-zoom";
328
+ const attrs = [
329
+ "clip-path",
330
+ "fill",
331
+ "mask",
332
+ "marker-start",
333
+ "marker-mid",
334
+ "marker-end"
335
+ ];
336
+ const idMap = /* @__PURE__ */ new Map();
337
+ if (svgEl.hasAttribute("id")) {
338
+ const { id: oldId } = svgEl;
339
+ const newId = oldId + newIdSuffix;
340
+ idMap.set(oldId, newId);
341
+ const svgNode = svgEl;
342
+ svgNode.id = newId;
343
+ }
344
+ svgEl.querySelectorAll("[id]").forEach((el) => {
345
+ const { id: oldId } = el;
346
+ const newId = oldId + newIdSuffix;
347
+ idMap.set(oldId, newId);
348
+ const node = el;
349
+ node.id = newId;
350
+ });
351
+ idMap.forEach((newId, oldId) => {
352
+ const urlOldID = `url(#${oldId})`;
353
+ const urlNewID = `url(#${newId})`;
354
+ const attrsQuery = attrs.map((attr) => `[${attr}="${urlOldID}"]`).join(", ");
355
+ svgEl.querySelectorAll(attrsQuery).forEach((usedEl) => {
356
+ attrs.forEach((attr) => {
357
+ if (usedEl.getAttribute(attr) === urlOldID) usedEl.setAttribute(attr, urlNewID);
358
+ });
359
+ });
360
+ });
361
+ svgEl.querySelectorAll("style").forEach((styleEl) => {
362
+ idMap.forEach((newId, oldId) => {
363
+ const { textContent } = styleEl;
364
+ if (textContent !== "") {
365
+ const styleNode = styleEl;
366
+ styleNode.textContent = textContent.replaceAll(`#${oldId}`, `#${newId}`);
367
+ }
368
+ });
369
+ });
370
+ };
371
+ //#endregion
372
+ export { adjustSvgIDs, getImgAlt, getImgSrc, getStyleGhost, getStyleModalImg, testDiv, testImg, testImgLoaded, testSvg };
@@ -1,9 +1,9 @@
1
1
  import { SearchProviderProps } from "../contexts/search.js";
2
2
  import { DefaultSearchDialogProps } from "../components/dialog/search-default.js";
3
3
  import { I18nProviderProps } from "../contexts/i18n.js";
4
- import { ComponentPropsWithoutRef, ReactNode } from "react";
4
+ import { ReactNode } from "react";
5
5
  import * as _$react_jsx_runtime0 from "react/jsx-runtime";
6
- import { ThemeProvider } from "next-themes";
6
+ import { ThemeProviderProps, UseThemeProps, useTheme } from "next-themes";
7
7
 
8
8
  //#region src/provider/base.d.ts
9
9
  interface SearchOptions extends Omit<SearchProviderProps, 'children'> {
@@ -15,6 +15,14 @@ interface SearchOptions extends Omit<SearchProviderProps, 'children'> {
15
15
  */
16
16
  enabled?: boolean;
17
17
  }
18
+ interface ThemeOptions extends ThemeProviderProps {
19
+ /**
20
+ * Enable `next-themes`
21
+ *
22
+ * @defaultValue true
23
+ */
24
+ enabled?: boolean;
25
+ }
18
26
  interface RootProviderProps {
19
27
  /**
20
28
  * `dir` option for Base UI
@@ -25,16 +33,9 @@ interface RootProviderProps {
25
33
  */
26
34
  search?: Partial<SearchOptions>;
27
35
  /**
28
- * Customise options of `next-themes`
36
+ * Customise options for `next-themes`
29
37
  */
30
- theme?: Partial<ComponentPropsWithoutRef<typeof ThemeProvider>> & {
31
- /**
32
- * Enable `next-themes`
33
- *
34
- * @defaultValue true
35
- */
36
- enabled?: boolean;
37
- };
38
+ theme?: ThemeOptions;
38
39
  i18n?: Omit<I18nProviderProps, 'children'>;
39
40
  children?: ReactNode;
40
41
  }
@@ -46,4 +47,4 @@ declare function RootProvider({
46
47
  i18n
47
48
  }: RootProviderProps): _$react_jsx_runtime0.JSX.Element;
48
49
  //#endregion
49
- export { RootProvider, RootProviderProps };
50
+ export { RootProvider, RootProviderProps, type UseThemeProps, useTheme };
@@ -3,7 +3,7 @@ import { I18nProvider } from "../contexts/i18n.js";
3
3
  import { SearchProvider } from "../contexts/search.js";
4
4
  import { lazy } from "react";
5
5
  import { jsx } from "react/jsx-runtime";
6
- import { ThemeProvider } from "next-themes";
6
+ import { ThemeProvider, useTheme } from "next-themes";
7
7
  import { DirectionProvider } from "@base-ui/react/direction-provider";
8
8
  //#region src/provider/base.tsx
9
9
  const DefaultSearchDialog = lazy(() => import("../components/dialog/search-default.js"));
@@ -32,4 +32,4 @@ function RootProvider({ children, dir = "ltr", theme = {}, search, i18n }) {
32
32
  });
33
33
  }
34
34
  //#endregion
35
- export { RootProvider };
35
+ export { RootProvider, useTheme };
package/dist/style.css CHANGED
@@ -1814,6 +1814,9 @@
1814
1814
  .underline {
1815
1815
  text-decoration-line: underline;
1816
1816
  }
1817
+ .opacity-\(--opacity\,0\) {
1818
+ opacity: var(--opacity,0);
1819
+ }
1817
1820
  .opacity-0 {
1818
1821
  opacity: 0%;
1819
1822
  }
@@ -1885,8 +1888,8 @@
1885
1888
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
1886
1889
  transition-duration: var(--tw-duration, var(--default-transition-duration));
1887
1890
  }
1888
- .transition-\[offset-distance\] {
1889
- transition-property: offset-distance;
1891
+ .transition-\[opacity\,offset-distance\] {
1892
+ transition-property: opacity,offset-distance;
1890
1893
  transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
1891
1894
  transition-duration: var(--tw-duration, var(--default-transition-duration));
1892
1895
  }
@@ -2004,6 +2007,9 @@
2004
2007
  .\[grid-area\:toc\] {
2005
2008
  grid-area: toc;
2006
2009
  }
2010
+ .\[offset-distance\:var\(--offset-distance\,0\)\] {
2011
+ offset-distance: var(--offset-distance,0);
2012
+ }
2007
2013
  .\[scrollbar-width\:none\] {
2008
2014
  scrollbar-width: none;
2009
2015
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fumadocs/base-ui",
3
- "version": "16.7.13",
3
+ "version": "16.7.15",
4
4
  "description": "The Base UI version of Fumadocs UI",
5
5
  "keywords": [
6
6
  "Docs",
@@ -11,8 +11,8 @@
11
11
  "author": "Fuma Nama",
12
12
  "repository": "github:fuma-nama/fumadocs",
13
13
  "files": [
14
- "css/*",
15
- "dist/*"
14
+ "css",
15
+ "dist"
16
16
  ],
17
17
  "type": "module",
18
18
  "exports": {
@@ -124,9 +124,10 @@
124
124
  "react-remove-scroll": "^2.7.2",
125
125
  "rehype-raw": "^7.0.0",
126
126
  "scroll-into-view-if-needed": "^3.1.0",
127
+ "shiki": "^4.0.2",
127
128
  "tailwind-merge": "^3.5.0",
128
129
  "unist-util-visit": "^5.1.0",
129
- "@fumadocs/tailwind": "0.0.4"
130
+ "@fumadocs/tailwind": "0.0.5"
130
131
  },
131
132
  "devDependencies": {
132
133
  "@tailwindcss/cli": "^4.2.2",
@@ -138,12 +139,11 @@
138
139
  "@types/react-dom": "^19.2.3",
139
140
  "fuma-cli": "^0.0.5",
140
141
  "react-medium-image-zoom": "^5.4.3",
141
- "shiki": "^4.0.2",
142
142
  "tailwindcss": "^4.2.2",
143
143
  "tsdown": "0.21.7",
144
144
  "unified": "^11.0.5",
145
- "@fumadocs/cli": "1.3.6",
146
- "fumadocs-core": "16.7.13",
145
+ "@fumadocs/cli": "1.3.7",
146
+ "fumadocs-core": "16.7.15",
147
147
  "tsconfig": "0.0.0"
148
148
  },
149
149
  "peerDependencies": {
@@ -153,13 +153,9 @@
153
153
  "next": "16.x.x",
154
154
  "react": "^19.2.0",
155
155
  "react-dom": "^19.2.0",
156
- "shiki": "*",
157
- "fumadocs-core": "16.7.13"
156
+ "fumadocs-core": "16.7.15"
158
157
  },
159
158
  "peerDependenciesMeta": {
160
- "shiki": {
161
- "optional": true
162
- },
163
159
  "next": {
164
160
  "optional": true
165
161
  },