@umami/shiso 1.1.8 → 1.1.10

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/mdx.config.ts CHANGED
@@ -101,6 +101,43 @@ function rehypeRemoveHighlightingFragments() {
101
101
  };
102
102
  }
103
103
 
104
+ function rehypeZoomableImages() {
105
+ return (tree: MdNode) => {
106
+ const visit = (node: MdNode, insideLink = false) => {
107
+ const isMdxElement = node.type === 'mdxJsxFlowElement' || node.type === 'mdxJsxTextElement';
108
+ const isLink = node.tagName === 'a' || (isMdxElement && node.name === 'a');
109
+ const linked = insideLink || isLink;
110
+
111
+ if (isMdxElement && node.name === 'img') {
112
+ node.name = 'ZoomableImage';
113
+
114
+ if (linked) {
115
+ const attributes = Array.isArray(node.attributes) ? node.attributes : [];
116
+ const hasNoZoom = attributes.some(
117
+ attribute =>
118
+ typeof attribute === 'object' && attribute !== null && attribute.name === 'noZoom',
119
+ );
120
+
121
+ if (!hasNoZoom) {
122
+ node.attributes = [
123
+ ...attributes,
124
+ { type: 'mdxJsxAttribute', name: 'noZoom', value: null },
125
+ ];
126
+ }
127
+ }
128
+ } else if (node.tagName === 'img' && linked) {
129
+ node.properties = { ...(node.properties as Record<string, unknown>), noZoom: true };
130
+ }
131
+
132
+ node.children?.forEach(child => {
133
+ visit(child, linked);
134
+ });
135
+ };
136
+
137
+ visit(tree);
138
+ };
139
+ }
140
+
104
141
  /**
105
142
  * The MDX compilation pipeline, shared by the app build and the test runner so
106
143
  * tests exercise the same transforms the site ships with.
@@ -122,6 +159,7 @@ export function shisoMdx(): Plugin {
122
159
  rehypeWrapJsxForHighlighting,
123
160
  rehypeHighlight,
124
161
  rehypeRemoveHighlightingFragments,
162
+ rehypeZoomableImages,
125
163
  rehypeSlug,
126
164
  [
127
165
  // Must follow rehypeSlug: the anchor href points at the id it sets.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umami/shiso",
3
- "version": "1.1.8",
3
+ "version": "1.1.10",
4
4
  "description": "Open-source documentation framework for Markdown and MDX sites.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/App.tsx CHANGED
@@ -16,6 +16,7 @@ import { DocPage } from '@/pages/DocPage';
16
16
 
17
17
  const mdxComponents = {
18
18
  ...docsComponents,
19
+ img: docsComponents.ZoomableImage,
19
20
  pre: CodeBlock,
20
21
  };
21
22
 
@@ -53,11 +53,7 @@ export function Header({ site }: { site: SiteModel }) {
53
53
  <img src={logo.light} alt="" className="h-6 w-auto dark:hidden" />
54
54
  ) : null}
55
55
  {logo?.dark ? (
56
- <img
57
- src={logo.dark}
58
- alt=""
59
- className={`hidden h-6 w-auto dark:block ${logo.light === logo.dark ? 'dark:invert' : ''}`}
60
- />
56
+ <img src={logo.dark} alt="" className="hidden h-6 w-auto dark:block" />
61
57
  ) : null}
62
58
  {name ? <span>{name}</span> : null}
63
59
  </a>
@@ -46,7 +46,9 @@ export function Frame({ caption, hint, children }: FrameProps) {
46
46
  </div>
47
47
  ) : null}
48
48
  <figure className={styles.frame}>
49
- <div className={styles.frameContent}>{enhanceAutoplayVideos(children)}</div>
49
+ <div data-slot="frame-content" className={styles.frameContent}>
50
+ {enhanceAutoplayVideos(children)}
51
+ </div>
50
52
  {caption ? (
51
53
  <figcaption className={styles.frameCaption}>{renderFrameText(caption)}</figcaption>
52
54
  ) : null}
@@ -0,0 +1,79 @@
1
+ import { XIcon } from 'lucide-react';
2
+ import type { ImgHTMLAttributes } from 'react';
3
+ import {
4
+ Dialog,
5
+ DialogClose,
6
+ DialogContent,
7
+ DialogTitle,
8
+ DialogTrigger,
9
+ } from '@/components/ui/dialog';
10
+ import { cn } from '@/lib/utils';
11
+
12
+ export interface ZoomableImageProps extends ImgHTMLAttributes<HTMLImageElement> {
13
+ noZoom?: boolean;
14
+ }
15
+
16
+ export function ZoomableImage({
17
+ noZoom = false,
18
+ className,
19
+ alt,
20
+ src,
21
+ ...props
22
+ }: ZoomableImageProps) {
23
+ const image = (
24
+ <img
25
+ {...props}
26
+ src={src}
27
+ alt={alt}
28
+ className={cn('block h-auto max-w-full rounded-lg', noZoom && 'my-6', className)}
29
+ />
30
+ );
31
+
32
+ if (noZoom || !src) {
33
+ return image;
34
+ }
35
+
36
+ const label = alt ? `View “${alt}” full size` : 'View image full size';
37
+
38
+ return (
39
+ <Dialog>
40
+ <DialogTrigger
41
+ render={
42
+ <button
43
+ type="button"
44
+ data-slot="zoomable-image"
45
+ className="my-6 block w-fit max-w-full cursor-zoom-in rounded-lg text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50"
46
+ aria-label={label}
47
+ />
48
+ }
49
+ >
50
+ {image}
51
+ </DialogTrigger>
52
+ <DialogContent
53
+ showCloseButton={false}
54
+ overlayClassName="bg-black/80 supports-backdrop-filter:backdrop-blur-sm"
55
+ className="flex max-h-[calc(100vh-2rem)] w-auto max-w-[calc(100vw-2rem)] items-center justify-center bg-transparent p-0 ring-0 shadow-none sm:max-w-[calc(100vw-2rem)]"
56
+ >
57
+ <DialogTitle className="sr-only">{alt || 'Full-size image'}</DialogTitle>
58
+ <img
59
+ src={src}
60
+ srcSet={props.srcSet}
61
+ sizes={props.sizes}
62
+ alt={alt || ''}
63
+ className="max-h-[calc(100vh-2rem)] max-w-[calc(100vw-2rem)] rounded-lg object-contain"
64
+ />
65
+ <DialogClose
66
+ render={
67
+ <button
68
+ type="button"
69
+ className="fixed top-4 right-4 inline-flex size-9 items-center justify-center rounded-full bg-black/60 text-white outline-none backdrop-blur-sm hover:bg-black/80 focus-visible:ring-3 focus-visible:ring-white/60"
70
+ aria-label="Close full-size image"
71
+ />
72
+ }
73
+ >
74
+ <XIcon className="size-5" />
75
+ </DialogClose>
76
+ </DialogContent>
77
+ </Dialog>
78
+ );
79
+ }
@@ -13,3 +13,4 @@ export * from './ResponseField';
13
13
  export * from './Steps';
14
14
  export * from './Tabs';
15
15
  export * from './Tooltip';
16
+ export * from './ZoomableImage';
@@ -58,7 +58,7 @@ export const styles = {
58
58
  'flex h-5 shrink-0 items-center text-muted-foreground [&_svg]:size-4 [&_svg]:fill-current',
59
59
  frame: 'relative overflow-hidden rounded-2xl border border-border bg-muted/25 p-2',
60
60
  frameContent:
61
- 'relative flex justify-center overflow-hidden rounded-xl [&_p]:m-0 [&_img]:w-full [&_video]:w-full',
61
+ 'relative flex justify-center overflow-hidden rounded-xl [&_p]:m-0 [&_img]:m-0 [&_img]:w-full [&_video]:w-full [&_[data-slot=zoomable-image]]:m-0 [&_[data-slot=zoomable-image]]:w-full',
62
62
  frameCaption:
63
63
  'relative mt-3 px-4 pb-1 text-center text-sm leading-5 text-muted-foreground [&_p]:m-0 [&_a]:font-semibold',
64
64