@san-siva/blogkit 1.1.36 → 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.
@@ -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 +1 @@
1
- {"version":3,"file":"MermaidViewport.d.ts","sourceRoot":"","sources":["../../../src/components/MermaidViewport.tsx"],"names":[],"mappings":"AAAA,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,4CAmBtB,CAAC;AAEF,eAAe,eAAe,CAAC"}
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"}
@@ -17,6 +17,7 @@ export declare const usePanZoom: ({ initialTransform, minScale, maxScale, zoomSt
17
17
  onMouseUp: () => void;
18
18
  zoomIn: () => void;
19
19
  zoomOut: () => void;
20
+ zoomAtPoint: (deltaY: number, pointX: number, pointY: number) => void;
20
21
  reset: () => void;
21
22
  };
22
23
  export type PanZoom = ReturnType<typeof usePanZoom>;
@@ -1 +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;;;;;CA8CrB,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC"}
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.36",
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",
@@ -1,3 +1,5 @@
1
+ import { useEffect, useRef } from 'react';
2
+
1
3
  import type { PanZoom } from '../hooks/usePanZoom';
2
4
 
3
5
  interface MermaidViewportProps {
@@ -16,25 +18,44 @@ const MermaidViewport = ({
16
18
  contentRef,
17
19
  contentId,
18
20
  hidden,
19
- }: MermaidViewportProps) => (
20
- <div
21
- className={`${className} ${pan.isDragging ? draggingClassName : ''}`}
22
- style={hidden ? { display: 'none' } : undefined}
23
- onMouseDown={pan.onMouseDown}
24
- onMouseMove={pan.onMouseMove}
25
- onMouseUp={pan.onMouseUp}
26
- onMouseLeave={pan.onMouseUp}
27
- >
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 (
28
39
  <div
29
- style={{
30
- transform: `translate(${pan.transform.x}px, ${pan.transform.y}px) scale(${pan.transform.scale})`,
31
- transformOrigin: '0 0',
32
- transition: pan.isDragging ? 'none' : 'transform 0.1s ease',
33
- }}
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}
34
47
  >
35
- <div ref={contentRef} id={contentId} />
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>
36
57
  </div>
37
- </div>
38
- );
58
+ );
59
+ };
39
60
 
40
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
  };
@@ -86,6 +86,26 @@ export const usePanZoom = ({
86
86
  }));
87
87
  }, [apply, zoomStep, minScale]);
88
88
 
89
+ const zoomAtPoint = useCallback(
90
+ (deltaY: number, pointX: number, pointY: number) => {
91
+ apply(prev => {
92
+ const factor = deltaY < 0 ? 1 + zoomStep : 1 - zoomStep;
93
+ const newScale = Math.max(
94
+ minScale,
95
+ Math.min(maxScale, prev.scale * factor)
96
+ );
97
+ if (newScale === prev.scale) return prev;
98
+ const ratio = newScale / prev.scale;
99
+ return {
100
+ scale: newScale,
101
+ x: pointX - (pointX - prev.x) * ratio,
102
+ y: pointY - (pointY - prev.y) * ratio,
103
+ };
104
+ });
105
+ },
106
+ [apply, zoomStep, minScale, maxScale]
107
+ );
108
+
89
109
  const reset = useCallback(() => {
90
110
  apply(() => initialTransform);
91
111
  }, [apply, initialTransform]);
@@ -98,6 +118,7 @@ export const usePanZoom = ({
98
118
  onMouseUp,
99
119
  zoomIn,
100
120
  zoomOut,
121
+ zoomAtPoint,
101
122
  reset,
102
123
  };
103
124
  };
@@ -85,6 +85,41 @@ $code-block-background-color: #282a36;
85
85
  color: stylekit.$color--code;
86
86
  }
87
87
 
88
+ &__actions {
89
+ display: flex;
90
+ flex-direction: row;
91
+ align-items: center;
92
+ gap: stylekit.rem(4);
93
+ }
94
+
95
+ &__expand {
96
+ position: relative;
97
+ height: stylekit.rem(32);
98
+ width: stylekit.rem(32);
99
+ cursor: pointer;
100
+ transition: all 0.2s ease-in-out;
101
+
102
+ &::before {
103
+ content: '';
104
+ position: absolute;
105
+ inset: 0;
106
+ background-image: url('../assets/expand.svg');
107
+ background-size: stylekit.rem(14) stylekit.rem(14);
108
+ background-position: center;
109
+ background-repeat: no-repeat;
110
+ filter: invert(1);
111
+ }
112
+
113
+ &:hover {
114
+ background-color: stylekit.$color--grey-dark;
115
+ border-radius: 100%;
116
+ }
117
+
118
+ &--collapse::before {
119
+ background-image: url('../assets/collapse.svg');
120
+ }
121
+ }
122
+
88
123
  &__copy {
89
124
  height: stylekit.rem(32);
90
125
  width: stylekit.rem(32);
@@ -106,6 +141,69 @@ $code-block-background-color: #282a36;
106
141
  }
107
142
  }
108
143
  }
144
+
145
+ &__modal {
146
+ position: fixed;
147
+ inset: 0;
148
+ z-index: 9999;
149
+ background-color: rgba(0, 0, 0, 0.7);
150
+ display: flex;
151
+ align-items: stretch;
152
+ justify-content: stretch;
153
+
154
+ &__content {
155
+ background-color: $code-block-background-color;
156
+ width: 100%;
157
+ height: 100%;
158
+ display: flex;
159
+ flex-direction: column;
160
+ position: relative;
161
+ }
162
+
163
+ &__wrapper {
164
+ flex: 1;
165
+ font-family: stylekit.$font-family--code;
166
+ overflow: auto;
167
+
168
+ scrollbar-width: thin;
169
+ scrollbar-color: stylekit.$color--grey-dark $code-block-background-color;
170
+
171
+ &::-webkit-scrollbar {
172
+ height: 8px;
173
+ width: 8px;
174
+ }
175
+
176
+ &::-webkit-scrollbar-track {
177
+ background: $code-block-background-color;
178
+ }
179
+
180
+ &::-webkit-scrollbar-thumb {
181
+ background-color: stylekit.$color--grey-dark;
182
+ border-radius: 4px;
183
+ }
184
+
185
+ padding: stylekit.rem(18);
186
+ tab-size: 2;
187
+
188
+ > pre {
189
+ margin: 0;
190
+ padding: 0 !important;
191
+ width: max-content;
192
+ min-width: 100%;
193
+
194
+ > code,
195
+ > code > * {
196
+ font-family: stylekit.$font-family--code;
197
+ font-size: stylekit.$font-size--small;
198
+ font-weight: unset;
199
+ font-style: unset;
200
+ line-height: stylekit.$line-height--normal;
201
+ color: unset;
202
+ text-align: left !important;
203
+ }
204
+ }
205
+ }
206
+ }
109
207
  }
110
208
 
111
209
  .margin-top--1 {
@@ -5,8 +5,14 @@ declare const styles: {
5
5
  readonly 'code-block__wrapper': string;
6
6
  readonly 'code-block__header': string;
7
7
  readonly 'code-block__header__title': string;
8
+ readonly 'code-block__header__actions': string;
9
+ readonly 'code-block__header__expand': string;
10
+ readonly 'code-block__header__expand--collapse': string;
8
11
  readonly 'code-block__header__copy': string;
9
12
  readonly 'code-block__header__copy--active': string;
13
+ readonly 'code-block__modal': string;
14
+ readonly 'code-block__modal__content': string;
15
+ readonly 'code-block__modal__wrapper': string;
10
16
  readonly 'code-block--static': string;
11
17
  readonly 'margin-top--1': string;
12
18
  readonly 'margin-bottom--2': string;