@san-siva/blogkit 1.1.36 → 1.1.38

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.38",
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,76 @@ $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
+ code {
155
+ font-size: unset;
156
+ font-weight: unset;
157
+ background-color: unset;
158
+ padding: unset;
159
+ }
160
+
161
+ &__content {
162
+ background-color: $code-block-background-color;
163
+ width: 100%;
164
+ height: 100%;
165
+ display: flex;
166
+ flex-direction: column;
167
+ position: relative;
168
+ }
169
+
170
+ &__wrapper {
171
+ flex: 1;
172
+ font-family: stylekit.$font-family--code;
173
+ overflow: auto;
174
+
175
+ scrollbar-width: thin;
176
+ scrollbar-color: stylekit.$color--grey-dark $code-block-background-color;
177
+
178
+ &::-webkit-scrollbar {
179
+ height: 8px;
180
+ width: 8px;
181
+ }
182
+
183
+ &::-webkit-scrollbar-track {
184
+ background: $code-block-background-color;
185
+ }
186
+
187
+ &::-webkit-scrollbar-thumb {
188
+ background-color: stylekit.$color--grey-dark;
189
+ border-radius: 4px;
190
+ }
191
+
192
+ padding: stylekit.rem(18);
193
+ tab-size: 2;
194
+
195
+ > pre {
196
+ margin: 0;
197
+ padding: 0 !important;
198
+ width: max-content;
199
+ min-width: 100%;
200
+
201
+ > code,
202
+ > code > * {
203
+ font-family: stylekit.$font-family--code;
204
+ font-size: stylekit.$font-size--small;
205
+ font-weight: unset;
206
+ font-style: unset;
207
+ line-height: stylekit.$line-height--normal;
208
+ color: unset;
209
+ text-align: left !important;
210
+ }
211
+ }
212
+ }
213
+ }
109
214
  }
110
215
 
111
216
  .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;