lupine.press 1.0.0
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/README.md +79 -0
- package/package.json +18 -0
- package/src/components/index.ts +7 -0
- package/src/components/lang-switcher.tsx +38 -0
- package/src/components/press-content.tsx +146 -0
- package/src/components/press-header.tsx +80 -0
- package/src/components/press-heading.tsx +46 -0
- package/src/components/press-home.tsx +109 -0
- package/src/components/press-layout.tsx +46 -0
- package/src/components/press-sidemenu.tsx +74 -0
- package/src/frames/index.ts +1 -0
- package/src/frames/press-frame.tsx +163 -0
- package/src/index.ts +7 -0
- package/src/page/index.ts +1 -0
- package/src/page/press-page.tsx +73 -0
- package/src/services/cache.ts +18 -0
- package/src/services/index.ts +3 -0
- package/src/services/markdown.ts +18 -0
- package/src/services/press-load.ts +17 -0
- package/src/styles/github.svg +3 -0
- package/src/styles/global.css +496 -0
- package/src/styles/lang.svg +3 -0
- package/src/styles/menu.svg +3 -0
- package/src/styles/press-themes.ts +14 -0
- package/src/styles/theme.svg +9 -0
- package/tsconfig.json +113 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
VNode,
|
|
3
|
+
CssProps,
|
|
4
|
+
MediaQueryRange,
|
|
5
|
+
bindLinks,
|
|
6
|
+
RefProps,
|
|
7
|
+
MediaQueryMaxWidth,
|
|
8
|
+
isFrontEnd,
|
|
9
|
+
} from 'lupine.components';
|
|
10
|
+
import { pressLoad } from '../services/press-load';
|
|
11
|
+
import { getSidebarScroll, setSidebarScroll } from '../services';
|
|
12
|
+
|
|
13
|
+
export interface PressFrameProps {
|
|
14
|
+
header: VNode<any>;
|
|
15
|
+
sidemenu: VNode<any>;
|
|
16
|
+
sidemenuWidth?: string;
|
|
17
|
+
hideSidemenu?: boolean;
|
|
18
|
+
content: VNode<any>;
|
|
19
|
+
css?: CssProps;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const PressFrame = (props: PressFrameProps) => {
|
|
23
|
+
if (isFrontEnd()) {
|
|
24
|
+
// Global navigation helper requested by user
|
|
25
|
+
(window as any).lpPressLoad = pressLoad;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const cssMarkdown: CssProps = {
|
|
29
|
+
// used in built markdown htmls, put under some element to override global.css
|
|
30
|
+
'h1, h2, h3': {
|
|
31
|
+
borderBottom: '1px solid var(--press-border-color)',
|
|
32
|
+
paddingBottom: '0.3em',
|
|
33
|
+
marginTop: '1.5em',
|
|
34
|
+
position: 'relative',
|
|
35
|
+
scrollMarginTop: '80px',
|
|
36
|
+
'&:first-child': { marginTop: 0 },
|
|
37
|
+
'&:hover .header-anchor': { opacity: 1 },
|
|
38
|
+
},
|
|
39
|
+
'.header-anchor': {
|
|
40
|
+
position: 'absolute',
|
|
41
|
+
left: '-1.5rem',
|
|
42
|
+
width: '1rem',
|
|
43
|
+
opacity: 0,
|
|
44
|
+
textDecoration: 'none',
|
|
45
|
+
color: 'var(--press-brand-color)',
|
|
46
|
+
fontSize: '0.8em',
|
|
47
|
+
transition: 'opacity 0.2s',
|
|
48
|
+
},
|
|
49
|
+
ol: {
|
|
50
|
+
listStyleType: 'decimal',
|
|
51
|
+
},
|
|
52
|
+
'li, p': {
|
|
53
|
+
margin: '0.5em 0',
|
|
54
|
+
},
|
|
55
|
+
pre: {
|
|
56
|
+
backgroundColor: 'var(--secondary-bg-color)',
|
|
57
|
+
padding: '1rem',
|
|
58
|
+
borderRadius: '8px',
|
|
59
|
+
overflowX: 'auto',
|
|
60
|
+
},
|
|
61
|
+
code: {
|
|
62
|
+
fontFamily: 'var(--font-family-mono, monospace)',
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const cssContainer: CssProps = {
|
|
67
|
+
display: 'flex',
|
|
68
|
+
flexDirection: 'column',
|
|
69
|
+
width: '100%',
|
|
70
|
+
height: '100%',
|
|
71
|
+
minHeight: '100%',
|
|
72
|
+
position: 'relative',
|
|
73
|
+
...cssMarkdown,
|
|
74
|
+
...props.css,
|
|
75
|
+
'.press-frame-box': {
|
|
76
|
+
display: 'flex',
|
|
77
|
+
flex: '1',
|
|
78
|
+
flexDirection: 'column',
|
|
79
|
+
height: '100%',
|
|
80
|
+
width: '100%',
|
|
81
|
+
maxWidth: MediaQueryMaxWidth.DesktopMax,
|
|
82
|
+
margin: 'auto',
|
|
83
|
+
// trick: to put two padding-top properties
|
|
84
|
+
'padding-top ': 'constant(safe-area-inset-top)',
|
|
85
|
+
'padding-top': 'env(safe-area-inset-top)',
|
|
86
|
+
},
|
|
87
|
+
'.press-frame-header': {
|
|
88
|
+
display: 'flex',
|
|
89
|
+
flexDirection: 'column',
|
|
90
|
+
alignItems: 'center',
|
|
91
|
+
padding: '4px 16px 4px 0px',
|
|
92
|
+
width: '100%',
|
|
93
|
+
},
|
|
94
|
+
'.press-frame-main': {
|
|
95
|
+
display: 'flex',
|
|
96
|
+
flex: '1',
|
|
97
|
+
flexDirection: 'row',
|
|
98
|
+
overflowY: 'auto',
|
|
99
|
+
scrollbarWidth: 'none',
|
|
100
|
+
borderTop: '1px solid var(--press-border-color)',
|
|
101
|
+
minHeight: '0',
|
|
102
|
+
scrollBehavior: 'smooth',
|
|
103
|
+
'&::-webkit-scrollbar': {
|
|
104
|
+
display: 'none',
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
'.press-frame-main .padding-block': {
|
|
108
|
+
padding: '0 16px',
|
|
109
|
+
},
|
|
110
|
+
'.press-frame-content': {
|
|
111
|
+
display: 'flex',
|
|
112
|
+
flex: '1',
|
|
113
|
+
flexDirection: 'column',
|
|
114
|
+
overflowY: 'auto',
|
|
115
|
+
scrollbarWidth: 'none',
|
|
116
|
+
},
|
|
117
|
+
'.press-frame-sidemenu': {
|
|
118
|
+
width: props.sidemenuWidth || '260px',
|
|
119
|
+
display: 'flex',
|
|
120
|
+
borderRight: '1px solid var(--press-border-color)',
|
|
121
|
+
overflowX: 'hidden',
|
|
122
|
+
overflowY: 'auto',
|
|
123
|
+
color: 'var(--sidebar-color)',
|
|
124
|
+
scrollbarWidth: 'none',
|
|
125
|
+
'&::-webkit-scrollbar': {
|
|
126
|
+
display: 'none',
|
|
127
|
+
},
|
|
128
|
+
// '&::-webkit-scrollbar': { width: '4px' },
|
|
129
|
+
// '&::-webkit-scrollbar-thumb': { backgroundColor: 'var(--press-border-color)' },
|
|
130
|
+
// backgroundColor: 'var(--sidebar-bg-color)',
|
|
131
|
+
},
|
|
132
|
+
[MediaQueryRange.TabletBelow]: {
|
|
133
|
+
'.press-frame-sidemenu': {
|
|
134
|
+
display: 'none',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const ref: RefProps = {
|
|
140
|
+
onLoad: async (el: Element) => {
|
|
141
|
+
bindLinks(el);
|
|
142
|
+
const sidemenu = el.querySelector('.press-frame-sidemenu');
|
|
143
|
+
if (sidemenu) {
|
|
144
|
+
sidemenu.scrollTop = getSidebarScroll();
|
|
145
|
+
sidemenu.addEventListener('scroll', () => {
|
|
146
|
+
setSidebarScroll(sidemenu.scrollTop);
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<div ref={ref} css={cssContainer} class='press-frame'>
|
|
154
|
+
<div class='press-frame-box'>
|
|
155
|
+
<div class='press-frame-header'>{props.header}</div>
|
|
156
|
+
<div class='press-frame-main'>
|
|
157
|
+
{!props.hideSidemenu && <div class='press-frame-sidemenu'>{props.sidemenu}</div>}
|
|
158
|
+
<div class='press-frame-content'>{props.content}</div>
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './press-page';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isFrontEnd, PageProps } from 'lupine.components';
|
|
2
|
+
import { PressLayout } from '../components';
|
|
3
|
+
import { getPressData } from '../services/cache';
|
|
4
|
+
|
|
5
|
+
export const PressPage = (props: PageProps) => {
|
|
6
|
+
if (!isFrontEnd()) {
|
|
7
|
+
return <div></div>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const markdownConfig = getPressData();
|
|
11
|
+
// Derive language from URL path first
|
|
12
|
+
const pathParts = window.location.pathname.split('/');
|
|
13
|
+
const pathLang = pathParts[1]; // /en/... or /zh/...
|
|
14
|
+
|
|
15
|
+
// Get supported langs from global config
|
|
16
|
+
const globalConfig = markdownConfig['/']?.data || {};
|
|
17
|
+
const supportedLangs = Array.isArray(globalConfig.lang) ? globalConfig.lang.map((l: any) => l.id) : ['en', 'zh'];
|
|
18
|
+
|
|
19
|
+
const langName = supportedLangs.includes(pathLang) ? pathLang : supportedLangs[0] || 'en';
|
|
20
|
+
|
|
21
|
+
// Sync global state with URL language
|
|
22
|
+
import('lupine.components').then((m) => {
|
|
23
|
+
if (m.getCurrentLang().langName !== langName) {
|
|
24
|
+
m.updateLang(langName);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// Get current path from router props or window.location
|
|
29
|
+
let currentPath = window.location.pathname;
|
|
30
|
+
if (currentPath === '' || currentPath === '/') {
|
|
31
|
+
currentPath = `/${langName}/index`;
|
|
32
|
+
} else {
|
|
33
|
+
if (currentPath.endsWith('/') && currentPath.length > 1)
|
|
34
|
+
currentPath = currentPath.substring(0, currentPath.length - 1);
|
|
35
|
+
// If it's just lang root like /en, append /index
|
|
36
|
+
if (currentPath === `/${langName}`) currentPath = `/${langName}/index`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const config = markdownConfig[currentPath];
|
|
40
|
+
const content = config ? config.html : `<h1>404 - Page Not Found</h1><p>Path: ${currentPath}</p>`;
|
|
41
|
+
|
|
42
|
+
// Get localized configuration from lang index
|
|
43
|
+
const rootIndex = markdownConfig[`/${langName}/index`]?.data || {};
|
|
44
|
+
const rootNav = rootIndex.nav || [];
|
|
45
|
+
const rootSidebar = rootIndex.sidebar || [];
|
|
46
|
+
const siteTitle = rootIndex.title || 'LupineJS';
|
|
47
|
+
|
|
48
|
+
// Collect langs from all supported language root index files
|
|
49
|
+
const langs = supportedLangs.map((l: string) => {
|
|
50
|
+
const data = markdownConfig[`/${l}/index`]?.data || {};
|
|
51
|
+
const langData = Array.isArray(data.lang) ? data.lang[0] : data.lang || {};
|
|
52
|
+
return {
|
|
53
|
+
text: langData.text || langData.title || langData.label || l.toUpperCase(),
|
|
54
|
+
id: l,
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<PressLayout
|
|
60
|
+
title={siteTitle}
|
|
61
|
+
nav={rootNav}
|
|
62
|
+
sidebar={rootSidebar}
|
|
63
|
+
lang={langName}
|
|
64
|
+
langs={langs}
|
|
65
|
+
data={config?.data}
|
|
66
|
+
headings={config?.headings}
|
|
67
|
+
sidemenuWidth={rootIndex['sidemenu-width']}
|
|
68
|
+
github={{ url: rootIndex['github-link'], title: rootIndex['github-title'] }}
|
|
69
|
+
>
|
|
70
|
+
{content}
|
|
71
|
+
</PressLayout>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { HtmlVar, VNode } from 'lupine.components';
|
|
2
|
+
|
|
3
|
+
const _cache: any = {};
|
|
4
|
+
export const bindPressData = (pressData: any) => {
|
|
5
|
+
_cache.data = pressData;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const getPressData = () => {
|
|
9
|
+
return _cache.data;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getSidebarScroll = () => {
|
|
13
|
+
return _cache.sidebarScroll || 0;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const setSidebarScroll = (val: number) => {
|
|
17
|
+
_cache.sidebarScroll = val;
|
|
18
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { marked } from 'marked';
|
|
2
|
+
import matter from 'gray-matter';
|
|
3
|
+
|
|
4
|
+
export interface MarkdownResult {
|
|
5
|
+
content: string;
|
|
6
|
+
data: { [key: string]: any };
|
|
7
|
+
html: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const parseMarkdown = (mdContent: string): MarkdownResult => {
|
|
11
|
+
const { data, content } = matter(mdContent);
|
|
12
|
+
const html = marked.parse(content) as string;
|
|
13
|
+
return {
|
|
14
|
+
content,
|
|
15
|
+
data,
|
|
16
|
+
html,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { getCurrentLang, initializePage } from 'lupine.components';
|
|
2
|
+
import { setSidebarScroll } from './cache';
|
|
3
|
+
|
|
4
|
+
export const pressLoad = (url: string) => {
|
|
5
|
+
const sidemenu = document.querySelector('.press-frame-sidemenu');
|
|
6
|
+
if (sidemenu) {
|
|
7
|
+
setSidebarScroll(sidemenu.scrollTop);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
let target = url;
|
|
11
|
+
if (target.startsWith('/') && !target.startsWith('/en/') && !target.startsWith('/zh/')) {
|
|
12
|
+
const { langName } = getCurrentLang();
|
|
13
|
+
target = `/${langName}${target}`;
|
|
14
|
+
}
|
|
15
|
+
if (target.endsWith('/') && target.length > 1) target = target.substring(0, target.length - 1);
|
|
16
|
+
initializePage(target || '/');
|
|
17
|
+
};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg viewBox='0 0 24 24' width='20' height='20' fill='currentColor'>
|
|
2
|
+
<path d='M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.164 6.839 9.49.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.604-3.369-1.34-3.369-1.34-.454-1.156-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.115 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.203 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12c0-5.523-4.477-10-10-10z' />
|
|
3
|
+
</svg>
|