@san-siva/blogkit 1.1.35 → 1.1.37

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 (56) hide show
  1. package/dist/assets/collapse.svg +16 -0
  2. package/dist/assets/expand.svg +4 -0
  3. package/dist/assets/reset.svg +4 -0
  4. package/dist/assets/zoom-in.svg +17 -0
  5. package/dist/assets/zoom-out.svg +17 -0
  6. package/dist/cjs/components/MermaidControls.js +17 -0
  7. package/dist/cjs/components/MermaidControls.js.map +1 -0
  8. package/dist/cjs/components/MermaidViewport.js +33 -0
  9. package/dist/cjs/components/MermaidViewport.js.map +1 -0
  10. package/dist/cjs/dynamicComponents/CodeBlockDynamic.js +28 -7
  11. package/dist/cjs/dynamicComponents/CodeBlockDynamic.js.map +1 -1
  12. package/dist/cjs/dynamicComponents/MermaidDynamic.js +52 -75
  13. package/dist/cjs/dynamicComponents/MermaidDynamic.js.map +1 -1
  14. package/dist/cjs/hooks/usePanZoom.js +92 -0
  15. package/dist/cjs/hooks/usePanZoom.js.map +1 -0
  16. package/dist/cjs/index.css +1 -1
  17. package/dist/cjs/index.css.map +1 -1
  18. package/dist/cjs/styles/CodeBlock.module.scss.js +1 -1
  19. package/dist/cjs/styles/Mermaid.module.scss.js +1 -1
  20. package/dist/esm/components/MermaidControls.js +13 -0
  21. package/dist/esm/components/MermaidControls.js.map +1 -0
  22. package/dist/esm/components/MermaidViewport.js +29 -0
  23. package/dist/esm/components/MermaidViewport.js.map +1 -0
  24. package/dist/esm/dynamicComponents/CodeBlockDynamic.js +29 -8
  25. package/dist/esm/dynamicComponents/CodeBlockDynamic.js.map +1 -1
  26. package/dist/esm/dynamicComponents/MermaidDynamic.js +52 -75
  27. package/dist/esm/dynamicComponents/MermaidDynamic.js.map +1 -1
  28. package/dist/esm/hooks/usePanZoom.js +90 -0
  29. package/dist/esm/hooks/usePanZoom.js.map +1 -0
  30. package/dist/esm/index.css +1 -1
  31. package/dist/esm/index.css.map +1 -1
  32. package/dist/esm/styles/CodeBlock.module.scss.js +1 -1
  33. package/dist/esm/styles/Mermaid.module.scss.js +1 -1
  34. package/dist/types/components/MermaidControls.d.ts +11 -0
  35. package/dist/types/components/MermaidControls.d.ts.map +1 -0
  36. package/dist/types/components/MermaidViewport.d.ts +12 -0
  37. package/dist/types/components/MermaidViewport.d.ts.map +1 -0
  38. package/dist/types/dynamicComponents/CodeBlockDynamic.d.ts.map +1 -1
  39. package/dist/types/dynamicComponents/MermaidDynamic.d.ts.map +1 -1
  40. package/dist/types/hooks/usePanZoom.d.ts +24 -0
  41. package/dist/types/hooks/usePanZoom.d.ts.map +1 -0
  42. package/package.json +1 -1
  43. package/src/assets/collapse.svg +16 -0
  44. package/src/assets/expand.svg +4 -0
  45. package/src/assets/reset.svg +4 -0
  46. package/src/assets/zoom-in.svg +17 -0
  47. package/src/assets/zoom-out.svg +17 -0
  48. package/src/components/MermaidControls.tsx +54 -0
  49. package/src/components/MermaidViewport.tsx +61 -0
  50. package/src/dynamicComponents/CodeBlockDynamic.tsx +83 -25
  51. package/src/dynamicComponents/MermaidDynamic.tsx +101 -131
  52. package/src/hooks/usePanZoom.ts +126 -0
  53. package/src/styles/CodeBlock.module.scss +98 -0
  54. package/src/styles/CodeBlock.module.scss.d.ts +6 -0
  55. package/src/styles/Mermaid.module.scss +72 -4
  56. package/src/styles/Mermaid.module.scss.d.ts +10 -0
@@ -1,4 +1,4 @@
1
- var styles = {"margin-top--1":"CodeBlock-module_margin-top--1__mApFz","margin-bottom--2":"CodeBlock-module_margin-bottom--2__fz9IX","code-block":"CodeBlock-module_code-block__ah1AG","code-block__wrapper":"CodeBlock-module_code-block__wrapper__-IUGO","code-block__header":"CodeBlock-module_code-block__header__93VEU","code-block__header__title":"CodeBlock-module_code-block__header__title__xxZgC","code-block__header__copy":"CodeBlock-module_code-block__header__copy__NCWSg","code-block__header__copy--active":"CodeBlock-module_code-block__header__copy--active__5ByAp","code-block--static":"CodeBlock-module_code-block--static__1gBzz"};
1
+ var styles = {"margin-top--1":"CodeBlock-module_margin-top--1__mApFz","margin-bottom--2":"CodeBlock-module_margin-bottom--2__fz9IX","code-block":"CodeBlock-module_code-block__ah1AG","code-block__wrapper":"CodeBlock-module_code-block__wrapper__-IUGO","code-block__header":"CodeBlock-module_code-block__header__93VEU","code-block__header__title":"CodeBlock-module_code-block__header__title__xxZgC","code-block__header__actions":"CodeBlock-module_code-block__header__actions__vClnL","code-block__header__expand":"CodeBlock-module_code-block__header__expand__PBJlP","code-block__header__expand--collapse":"CodeBlock-module_code-block__header__expand--collapse__93Kth","code-block__header__copy":"CodeBlock-module_code-block__header__copy__NCWSg","code-block__header__copy--active":"CodeBlock-module_code-block__header__copy--active__5ByAp","code-block__modal":"CodeBlock-module_code-block__modal__nNvcI","code-block__modal__content":"CodeBlock-module_code-block__modal__content__qWuic","code-block__modal__wrapper":"CodeBlock-module_code-block__modal__wrapper__me6hC","code-block--static":"CodeBlock-module_code-block--static__1gBzz"};
2
2
 
3
3
  export { styles as default };
4
4
  //# sourceMappingURL=CodeBlock.module.scss.js.map
@@ -1,4 +1,4 @@
1
- var styles = {"margin-top--1":"Mermaid-module_margin-top--1__W10B7","margin-bottom--2":"Mermaid-module_margin-bottom--2__w3Zct","mermaid":"Mermaid-module_mermaid__02iRo","mermaid__controls":"Mermaid-module_mermaid__controls__0YNFR","mermaid__controls__btn":"Mermaid-module_mermaid__controls__btn__w04sa","mermaid__viewport":"Mermaid-module_mermaid__viewport__KVL-A","mermaid__viewport--dragging":"Mermaid-module_mermaid__viewport--dragging__WjBzf"};
1
+ var styles = {"margin-top--1":"Mermaid-module_margin-top--1__W10B7","margin-bottom--2":"Mermaid-module_margin-bottom--2__w3Zct","mermaid":"Mermaid-module_mermaid__02iRo","mermaid__controls":"Mermaid-module_mermaid__controls__0YNFR","mermaid__controls__btn":"Mermaid-module_mermaid__controls__btn__w04sa","mermaid__controls__btn--zoom-in":"Mermaid-module_mermaid__controls__btn--zoom-in__KX5QE","mermaid__controls__btn--zoom-out":"Mermaid-module_mermaid__controls__btn--zoom-out__Zffvt","mermaid__controls__btn--reset":"Mermaid-module_mermaid__controls__btn--reset__njRg-","mermaid__controls__btn--expand":"Mermaid-module_mermaid__controls__btn--expand__ZriXV","mermaid__controls__btn--collapse":"Mermaid-module_mermaid__controls__btn--collapse__3z9Fo","mermaid__viewport":"Mermaid-module_mermaid__viewport__KVL-A","mermaid__viewport--dragging":"Mermaid-module_mermaid__viewport--dragging__WjBzf","mermaid__modal":"Mermaid-module_mermaid__modal__wkcDB","mermaid__modal__content":"Mermaid-module_mermaid__modal__content__LTlQS","mermaid__modal__controls":"Mermaid-module_mermaid__modal__controls__S3kxt","mermaid__modal__viewport":"Mermaid-module_mermaid__modal__viewport__wxddc","mermaid__modal__viewport--dragging":"Mermaid-module_mermaid__modal__viewport--dragging__fFm72"};
2
2
 
3
3
  export { styles as default };
4
4
  //# sourceMappingURL=Mermaid.module.scss.js.map
@@ -0,0 +1,11 @@
1
+ interface MermaidControlsProps {
2
+ className: string;
3
+ isExpanded: boolean;
4
+ onZoomIn: () => void;
5
+ onZoomOut: () => void;
6
+ onReset: () => void;
7
+ onToggleExpand: () => void;
8
+ }
9
+ declare const MermaidControls: ({ className, isExpanded, onZoomIn, onZoomOut, onReset, onToggleExpand, }: MermaidControlsProps) => import("react/jsx-runtime").JSX.Element;
10
+ export default MermaidControls;
11
+ //# sourceMappingURL=MermaidControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MermaidControls.d.ts","sourceRoot":"","sources":["../../../src/components/MermaidControls.tsx"],"names":[],"mappings":"AAEA,UAAU,oBAAoB;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED,QAAA,MAAM,eAAe,GAAI,0EAOtB,oBAAoB,4CAiCtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { PanZoom } from '../hooks/usePanZoom';
2
+ interface MermaidViewportProps {
3
+ className: string;
4
+ draggingClassName: string;
5
+ pan: PanZoom;
6
+ contentRef: React.RefObject<HTMLDivElement>;
7
+ contentId?: string;
8
+ hidden?: boolean;
9
+ }
10
+ declare const MermaidViewport: ({ className, draggingClassName, pan, contentRef, contentId, hidden, }: MermaidViewportProps) => import("react/jsx-runtime").JSX.Element;
11
+ export default MermaidViewport;
12
+ //# sourceMappingURL=MermaidViewport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MermaidViewport.d.ts","sourceRoot":"","sources":["../../../src/components/MermaidViewport.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAEnD,UAAU,oBAAoB;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,GAAG,EAAE,OAAO,CAAC;IACb,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,QAAA,MAAM,eAAe,GAAI,uEAOtB,oBAAoB,4CAsCtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"CodeBlockDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/CodeBlockDynamic.tsx"],"names":[],"mappings":"AAMA,OAAO,mCAAmC,CAAC;AAM3C,UAAU,UAAU;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,QAAA,MAAM,SAAS,GAAI,iDAKhB,UAAU,4CAiDZ,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"CodeBlockDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/CodeBlockDynamic.tsx"],"names":[],"mappings":"AAOA,OAAO,mCAAmC,CAAC;AAM3C,UAAU,UAAU;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AASD,QAAA,MAAM,SAAS,GAAI,iDAKhB,UAAU,4CAmGZ,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"MermaidDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/MermaidDynamic.tsx"],"names":[],"mappings":"AASA,UAAU,iBAAiB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAuBD,QAAA,MAAM,OAAO,GAAI,2CAKd,iBAAiB,4CAqKnB,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"MermaidDynamic.d.ts","sourceRoot":"","sources":["../../../src/dynamicComponents/MermaidDynamic.tsx"],"names":[],"mappings":"AAaA,UAAU,iBAAiB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;CACxB;AAmBD,QAAA,MAAM,OAAO,GAAI,2CAKd,iBAAiB,4CAuInB,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface Transform {
2
+ scale: number;
3
+ x: number;
4
+ y: number;
5
+ }
6
+ export interface UsePanZoomOptions {
7
+ initialTransform?: Transform;
8
+ minScale?: number;
9
+ maxScale?: number;
10
+ zoomStep?: number;
11
+ }
12
+ export declare const usePanZoom: ({ initialTransform, minScale, maxScale, zoomStep, }?: UsePanZoomOptions) => {
13
+ transform: Transform;
14
+ isDragging: boolean;
15
+ onMouseDown: (e: React.MouseEvent) => void;
16
+ onMouseMove: (e: React.MouseEvent) => void;
17
+ onMouseUp: () => void;
18
+ zoomIn: () => void;
19
+ zoomOut: () => void;
20
+ zoomAtPoint: (deltaY: number, pointX: number, pointY: number) => void;
21
+ reset: () => void;
22
+ };
23
+ export type PanZoom = ReturnType<typeof usePanZoom>;
24
+ //# sourceMappingURL=usePanZoom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePanZoom.d.ts","sourceRoot":"","sources":["../../../src/hooks/usePanZoom.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IACjC,gBAAgB,CAAC,EAAE,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,UAAU,GAAI,sDAKxB,iBAAsB;;;qBAkBY,KAAK,CAAC,UAAU;qBAa/C,KAAK,CAAC,UAAU;;;;0BAiCX,MAAM,UAAU,MAAM,UAAU,MAAM;;CAkChD,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@san-siva/blogkit",
3
- "version": "1.1.35",
3
+ "version": "1.1.37",
4
4
  "description": "A reusable blog component library for React/Next.js applications with code highlighting, diagrams, and rich content features",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,16 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+
3
+ <svg width="800px" height="800px" viewBox="0 0 48 48" fill="#313030" xmlns="http://www.w3.org/2000/svg">
4
+ <title>collapse-fullscreen</title>
5
+ <g id="Layer_2" data-name="Layer 2">
6
+ <g id="invisible_box" data-name="invisible box">
7
+ <rect width="48" height="48" fill="none"/>
8
+ </g>
9
+ <g id="icons_Q2" data-name="icons Q2">
10
+ <g>
11
+ <path d="M8,26a2,2,0,0,0-2,2.3A2.1,2.1,0,0,0,8.1,30h7.1L4.7,40.5a2,2,0,0,0-.2,2.8A1.8,1.8,0,0,0,6,44a2,2,0,0,0,1.4-.6L18,32.8v7.1A2.1,2.1,0,0,0,19.7,42,2,2,0,0,0,22,40V28a2,2,0,0,0-2-2Z"/>
12
+ <path d="M43.7,4.8a2,2,0,0,0-3.1-.2L30,15.2V8.1A2.1,2.1,0,0,0,28.3,6,2,2,0,0,0,26,8V20a2,2,0,0,0,2,2H39.9A2.1,2.1,0,0,0,42,20.3,2,2,0,0,0,40,18H32.8L43.4,7.5A2.3,2.3,0,0,0,43.7,4.8Z"/>
13
+ </g>
14
+ </g>
15
+ </g>
16
+ </svg>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg width="800px" height="800px" viewBox="-2 -2 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M14 10L21 3M21 3H16.5M21 3V7.5M10 14L3 21M3 21H7.5M3 21L3 16.5" stroke="#313030" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
4
+ </svg>
@@ -0,0 +1,4 @@
1
+ <?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
2
+ <svg fill="#313030" width="800px" height="800px" viewBox="0 0 1920 1920" xmlns="http://www.w3.org/2000/svg">
3
+ <path d="M960 0v213.333c411.627 0 746.667 334.934 746.667 746.667S1371.627 1706.667 960 1706.667 213.333 1371.733 213.333 960c0-197.013 78.4-382.507 213.334-520.747v254.08H640V106.667H53.333V320h191.04C88.64 494.08 0 720.96 0 960c0 529.28 430.613 960 960 960s960-430.72 960-960S1489.387 0 960 0" fill-rule="evenodd"/>
4
+ </svg>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg width="800px" height="800px" viewBox="-1 -1 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
4
+
5
+ <title>zoom-in</title>
6
+ <desc>Created with Sketch Beta.</desc>
7
+ <defs>
8
+
9
+ </defs>
10
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
11
+ <g id="Icon-Set" sketch:type="MSLayerGroup" transform="translate(-308.000000, -1139.000000)" fill="#313030" stroke="#313030" stroke-width="1.5" stroke-linejoin="round">
12
+ <path d="M321.46,1163.45 C315.17,1163.45 310.07,1158.44 310.07,1152.25 C310.07,1146.06 315.17,1141.04 321.46,1141.04 C327.75,1141.04 332.85,1146.06 332.85,1152.25 C332.85,1158.44 327.75,1163.45 321.46,1163.45 L321.46,1163.45 Z M339.688,1169.25 L331.429,1161.12 C333.592,1158.77 334.92,1155.67 334.92,1152.25 C334.92,1144.93 328.894,1139 321.46,1139 C314.026,1139 308,1144.93 308,1152.25 C308,1159.56 314.026,1165.49 321.46,1165.49 C324.672,1165.49 327.618,1164.38 329.932,1162.53 L338.225,1170.69 C338.629,1171.09 339.284,1171.09 339.688,1170.69 C340.093,1170.3 340.093,1169.65 339.688,1169.25 L339.688,1169.25 Z M326.519,1151.41 L322.522,1151.41 L322.522,1147.41 C322.522,1146.85 322.075,1146.41 321.523,1146.41 C320.972,1146.41 320.524,1146.85 320.524,1147.41 L320.524,1151.41 L316.529,1151.41 C315.978,1151.41 315.53,1151.59 315.53,1152.14 C315.53,1152.7 315.978,1153.41 316.529,1153.41 L320.524,1153.41 L320.524,1157.41 C320.524,1157.97 320.972,1158.41 321.523,1158.41 C322.075,1158.41 322.522,1157.97 322.522,1157.41 L322.522,1153.41 L326.519,1153.41 C327.07,1153.41 327.518,1152.96 327.518,1152.41 C327.518,1151.86 327.07,1151.41 326.519,1151.41 L326.519,1151.41 Z" id="zoom-in" sketch:type="MSShapeGroup">
13
+
14
+ </path>
15
+ </g>
16
+ </g>
17
+ </svg>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+ <svg width="800px" height="800px" viewBox="-1 -1 34 34" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
4
+
5
+ <title>zoom-out</title>
6
+ <desc>Created with Sketch Beta.</desc>
7
+ <defs>
8
+
9
+ </defs>
10
+ <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
11
+ <g id="Icon-Set" sketch:type="MSLayerGroup" transform="translate(-360.000000, -1139.000000)" fill="#313030" stroke="#313030" stroke-width="1.5" stroke-linejoin="round">
12
+ <path d="M373.46,1163.45 C367.17,1163.45 362.071,1158.44 362.071,1152.25 C362.071,1146.06 367.17,1141.04 373.46,1141.04 C379.75,1141.04 384.85,1146.06 384.85,1152.25 C384.85,1158.44 379.75,1163.45 373.46,1163.45 L373.46,1163.45 Z M391.688,1169.25 L383.429,1161.12 C385.592,1158.77 386.92,1155.67 386.92,1152.25 C386.92,1144.93 380.894,1139 373.46,1139 C366.026,1139 360,1144.93 360,1152.25 C360,1159.56 366.026,1165.49 373.46,1165.49 C376.672,1165.49 379.618,1164.38 381.932,1162.53 L390.225,1170.69 C390.629,1171.09 391.284,1171.09 391.688,1170.69 C392.093,1170.3 392.093,1169.65 391.688,1169.25 L391.688,1169.25 Z M378.689,1151.41 L368.643,1151.41 C368.102,1151.41 367.663,1151.84 367.663,1152.37 C367.663,1152.9 368.102,1153.33 368.643,1153.33 L378.689,1153.33 C379.23,1153.33 379.669,1152.9 379.669,1152.37 C379.669,1151.84 379.23,1151.41 378.689,1151.41 L378.689,1151.41 Z" id="zoom-out" sketch:type="MSShapeGroup">
13
+
14
+ </path>
15
+ </g>
16
+ </g>
17
+ </svg>
@@ -0,0 +1,54 @@
1
+ import styles from '../styles/Mermaid.module.scss';
2
+
3
+ interface MermaidControlsProps {
4
+ className: string;
5
+ isExpanded: boolean;
6
+ onZoomIn: () => void;
7
+ onZoomOut: () => void;
8
+ onReset: () => void;
9
+ onToggleExpand: () => void;
10
+ }
11
+
12
+ const MermaidControls = ({
13
+ className,
14
+ isExpanded,
15
+ onZoomIn,
16
+ onZoomOut,
17
+ onReset,
18
+ onToggleExpand,
19
+ }: MermaidControlsProps) => {
20
+ const toggleLabel = isExpanded ? 'Close fullscreen' : 'Expand to fullscreen';
21
+ const toggleModifier = isExpanded
22
+ ? styles['mermaid__controls__btn--collapse']
23
+ : styles['mermaid__controls__btn--expand'];
24
+ return (
25
+ <div className={className}>
26
+ <button
27
+ className={`${styles['mermaid__controls__btn']} ${styles['mermaid__controls__btn--zoom-in']}`}
28
+ onClick={onZoomIn}
29
+ aria-label="Zoom in"
30
+ title="Zoom in"
31
+ />
32
+ <button
33
+ className={`${styles['mermaid__controls__btn']} ${styles['mermaid__controls__btn--zoom-out']}`}
34
+ onClick={onZoomOut}
35
+ aria-label="Zoom out"
36
+ title="Zoom out"
37
+ />
38
+ <button
39
+ className={`${styles['mermaid__controls__btn']} ${styles['mermaid__controls__btn--reset']}`}
40
+ onClick={onReset}
41
+ aria-label="Reset view"
42
+ title="Reset view"
43
+ />
44
+ <button
45
+ className={`${styles['mermaid__controls__btn']} ${toggleModifier}`}
46
+ onClick={onToggleExpand}
47
+ aria-label={toggleLabel}
48
+ title={toggleLabel}
49
+ />
50
+ </div>
51
+ );
52
+ };
53
+
54
+ export default MermaidControls;
@@ -0,0 +1,61 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import type { PanZoom } from '../hooks/usePanZoom';
4
+
5
+ interface MermaidViewportProps {
6
+ className: string;
7
+ draggingClassName: string;
8
+ pan: PanZoom;
9
+ contentRef: React.RefObject<HTMLDivElement>;
10
+ contentId?: string;
11
+ hidden?: boolean;
12
+ }
13
+
14
+ const MermaidViewport = ({
15
+ className,
16
+ draggingClassName,
17
+ pan,
18
+ contentRef,
19
+ contentId,
20
+ hidden,
21
+ }: MermaidViewportProps) => {
22
+ const viewportRef = useRef<HTMLDivElement>(null);
23
+ const { zoomAtPoint } = pan;
24
+
25
+ useEffect(() => {
26
+ const el = viewportRef.current;
27
+ if (!el) return;
28
+ const onWheel = (e: WheelEvent) => {
29
+ if (!e.ctrlKey) return;
30
+ e.preventDefault();
31
+ const rect = el.getBoundingClientRect();
32
+ zoomAtPoint(e.deltaY, e.clientX - rect.left, e.clientY - rect.top);
33
+ };
34
+ el.addEventListener('wheel', onWheel, { passive: false });
35
+ return () => el.removeEventListener('wheel', onWheel);
36
+ }, [zoomAtPoint]);
37
+
38
+ return (
39
+ <div
40
+ ref={viewportRef}
41
+ className={`${className} ${pan.isDragging ? draggingClassName : ''}`}
42
+ style={hidden ? { display: 'none' } : undefined}
43
+ onMouseDown={pan.onMouseDown}
44
+ onMouseMove={pan.onMouseMove}
45
+ onMouseUp={pan.onMouseUp}
46
+ onMouseLeave={pan.onMouseUp}
47
+ >
48
+ <div
49
+ style={{
50
+ transform: `translate(${pan.transform.x}px, ${pan.transform.y}px) scale(${pan.transform.scale})`,
51
+ transformOrigin: '0 0',
52
+ transition: pan.isDragging ? 'none' : 'transform 0.1s ease',
53
+ }}
54
+ >
55
+ <div ref={contentRef} id={contentId} />
56
+ </div>
57
+ </div>
58
+ );
59
+ };
60
+
61
+ export default MermaidViewport;
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
- import { useState } from 'react';
3
+ import { useEffect, useState } from 'react';
4
+ import { createPortal } from 'react-dom';
4
5
  import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
5
6
  import { dracula } from 'react-syntax-highlighter/dist/esm/styles/prism';
6
7
 
@@ -17,6 +18,13 @@ interface Properties {
17
18
  code?: string;
18
19
  }
19
20
 
21
+ const lineNumberStyle = {
22
+ color: '#95a1b1',
23
+ fontSize: '0.9em',
24
+ paddingRight: '1em',
25
+ marginRight: '8px',
26
+ };
27
+
20
28
  const CodeBlock = ({
21
29
  language = 'javascript',
22
30
  code = '',
@@ -24,6 +32,7 @@ const CodeBlock = ({
24
32
  hasMarginDown = false,
25
33
  }: Properties) => {
26
34
  const [isCopyMode, setCopyMode] = useState(false);
35
+ const [isExpanded, setIsExpanded] = useState(false);
27
36
 
28
37
  const copyToClipboard = async () => {
29
38
  try {
@@ -37,38 +46,87 @@ const CodeBlock = ({
37
46
  }
38
47
  };
39
48
 
40
- const lineNumberStyle = {
41
- color: '#95a1b1',
42
- fontSize: '0.9em',
43
- paddingRight: '1em',
44
- marginRight: '8px',
45
- };
49
+ useEffect(() => {
50
+ if (!isExpanded) return;
51
+ const onKey = (e: KeyboardEvent) => {
52
+ if (e.key === 'Escape') setIsExpanded(false);
53
+ };
54
+ const previousOverflow = document.body.style.overflow;
55
+ document.body.style.overflow = 'hidden';
56
+ window.addEventListener('keydown', onKey);
57
+ return () => {
58
+ document.body.style.overflow = previousOverflow;
59
+ window.removeEventListener('keydown', onKey);
60
+ };
61
+ }, [isExpanded]);
46
62
 
47
- return (
48
- <div
49
- className={`${styles['code-block']} ${hasMarginUp ? styles['margin-top--1'] : ''} ${
50
- hasMarginDown ? styles['margin-bottom--2'] : ''
51
- }`}
52
- >
53
- <div className={styles['code-block__header']}>
54
- <div className={styles['code-block__header__title']}>{language}</div>
63
+ const renderHeader = (expanded: boolean) => (
64
+ <div className={styles['code-block__header']}>
65
+ <div className={styles['code-block__header__title']}>{language}</div>
66
+ <div className={styles['code-block__header__actions']}>
67
+ <div
68
+ className={`${styles['code-block__header__expand']} ${
69
+ expanded ? styles['code-block__header__expand--collapse'] : ''
70
+ }`}
71
+ onClick={() => setIsExpanded(!expanded)}
72
+ role="button"
73
+ aria-label={expanded ? 'Close fullscreen' : 'Expand to fullscreen'}
74
+ title={expanded ? 'Close fullscreen' : 'Expand to fullscreen'}
75
+ />
55
76
  <div
56
77
  className={`${styles['code-block__header__copy']} ${
57
78
  isCopyMode ? styles['code-block__header__copy--active'] : ''
58
79
  }`}
59
80
  onClick={copyToClipboard}
81
+ role="button"
82
+ aria-label="Copy code"
83
+ title="Copy code"
60
84
  />
61
85
  </div>
62
- <div className={styles['code-block__wrapper']}>
63
- <SH
64
- language={language}
65
- style={dracula}
66
- showLineNumbers
67
- lineNumberStyle={lineNumberStyle}
68
- >
69
- {code}
70
- </SH>
71
- </div>
86
+ </div>
87
+ );
88
+
89
+ const renderHighlighter = () => (
90
+ <SH
91
+ language={language}
92
+ style={dracula}
93
+ showLineNumbers
94
+ lineNumberStyle={lineNumberStyle}
95
+ >
96
+ {code}
97
+ </SH>
98
+ );
99
+
100
+ return (
101
+ <div
102
+ className={`${styles['code-block']} ${hasMarginUp ? styles['margin-top--1'] : ''} ${
103
+ hasMarginDown ? styles['margin-bottom--2'] : ''
104
+ }`}
105
+ >
106
+ {renderHeader(false)}
107
+ <div className={styles['code-block__wrapper']}>{renderHighlighter()}</div>
108
+
109
+ {isExpanded &&
110
+ typeof document !== 'undefined' &&
111
+ createPortal(
112
+ <div
113
+ className={styles['code-block__modal']}
114
+ onClick={() => setIsExpanded(false)}
115
+ role="dialog"
116
+ aria-modal="true"
117
+ >
118
+ <div
119
+ className={styles['code-block__modal__content']}
120
+ onClick={e => e.stopPropagation()}
121
+ >
122
+ {renderHeader(true)}
123
+ <div className={styles['code-block__modal__wrapper']}>
124
+ {renderHighlighter()}
125
+ </div>
126
+ </div>
127
+ </div>,
128
+ document.body
129
+ )}
72
130
  </div>
73
131
  );
74
132
  };