lupine.web 1.1.4 → 1.1.5
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 +66 -3
- package/jsx-runtime/index.js +14 -14
- package/jsx-runtime/package.json +16 -16
- package/jsx-runtime/src/index.d.ts +2 -2
- package/package.json +53 -52
- package/src/core/bind-attributes.ts +61 -61
- package/src/core/bind-lang.ts +52 -52
- package/src/core/bind-links.ts +26 -16
- package/src/core/bind-meta.tsx +52 -52
- package/src/core/bind-ref.ts +51 -51
- package/src/core/bind-styles.ts +239 -239
- package/src/core/bind-theme.ts +53 -53
- package/src/core/camel-to-hyphens.ts +3 -3
- package/src/core/export-lupine.ts +80 -80
- package/src/core/index.ts +17 -17
- package/src/core/initialize.ts +116 -116
- package/src/core/mount-component.ts +72 -68
- package/src/core/page-loaded-events.ts +16 -16
- package/src/core/page-router.ts +180 -180
- package/src/core/render-component.ts +230 -233
- package/src/core/replace-innerhtml.ts +23 -23
- package/src/core/server-cookie.ts +24 -24
- package/src/global.d.ts +66 -66
- package/src/index.ts +14 -14
- package/src/jsx.ts +1044 -1043
- package/src/lib/cookie.ts +44 -44
- package/src/lib/debug-watch.ts +32 -32
- package/src/lib/index.ts +7 -7
- package/src/lib/is-frontend.ts +3 -3
- package/src/lib/logger.ts +55 -55
- package/src/lib/unique-id.ts +40 -40
- package/src/lib/web-config.ts +79 -79
- package/src/lib/web-env.ts +99 -99
- package/src/models/index.ts +4 -4
- package/src/models/json-props.ts +8 -8
- package/src/models/simple-storage-props.ts +9 -9
- package/src/models/theme-props.ts +7 -7
- package/src/models/to-client-delivery-props.ts +8 -8
- package/src/styles/css-styles.ts +814 -814
- package/src/styles/index.ts +4 -4
- package/tsconfig.json +113 -113
package/README.md
CHANGED
|
@@ -1,3 +1,66 @@
|
|
|
1
|
-
# lupine.web
|
|
2
|
-
|
|
3
|
-
lupine.web is a React-like, extremely fast, small
|
|
1
|
+
# lupine.web
|
|
2
|
+
|
|
3
|
+
**lupine.web** is a React-like, extremely fast, small-size, and lightweight frontend framework designed for modern web development. It focuses on performance, simplicity, and a full-stack experience when paired with `lupine.api`.
|
|
4
|
+
|
|
5
|
+
## Why lupine.web?
|
|
6
|
+
|
|
7
|
+
### 🚀 Zero-Dependency & Lightweight
|
|
8
|
+
|
|
9
|
+
We believe in keeping things simple. `lupine.web` has **zero external dependencies**, resulting in a tiny bundle size and lightning-fast load times. It uses TSX syntax, so if you know React, you already feel at home.
|
|
10
|
+
|
|
11
|
+
### 🎨 Built-in CSS-in-JS
|
|
12
|
+
|
|
13
|
+
Forget about setting up complex CSS loaders or external styling libraries. `lupine.web` comes with a powerful, built-in CSS-in-JS solution.
|
|
14
|
+
|
|
15
|
+
- **Scoped Styles**: Styles are automatically scoped to your components to prevent collisions.
|
|
16
|
+
- **Nesting Support**: Write cleaner CSS with nested selectors (e.g., `&:hover`, `& > span`).
|
|
17
|
+
- **Theming**: Native support for light/dark modes and custom themes.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
const MyButton = (props) => (
|
|
21
|
+
<button
|
|
22
|
+
css={{
|
|
23
|
+
backgroundColor: 'blue',
|
|
24
|
+
color: 'white',
|
|
25
|
+
'&:hover': { backgroundColor: 'darkblue' },
|
|
26
|
+
[MediaQueryRange.Mobile]: { width: '100%' },
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{props.children}
|
|
30
|
+
</button>
|
|
31
|
+
);
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 🛣️ Powerful Router
|
|
35
|
+
|
|
36
|
+
Our functional router is designed for flexibility and control.
|
|
37
|
+
|
|
38
|
+
- **Route Guards**: Easily implement authentication checks or permissions.
|
|
39
|
+
- **Nested Routes**: Organize your application with sub-routers for modular architecture.
|
|
40
|
+
- **SSR Ready**: Routes work seamlessly on both server and client.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const pageRouter = new PageRouter();
|
|
44
|
+
// Middleware/Guard example
|
|
45
|
+
pageRouter.setFilter(async (props) => {
|
|
46
|
+
if (!checkAuth(props)) return <Redirect to='/login' />;
|
|
47
|
+
return null; // Pass
|
|
48
|
+
});
|
|
49
|
+
pageRouter.use('/dashboard/*', DashboardRouter);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### ⚡ Server-Side Rendering (SSR) First
|
|
53
|
+
|
|
54
|
+
Visual performance is critical. `lupine.web` is built with SSR in mind from day one.
|
|
55
|
+
|
|
56
|
+
- **No Flashing**: Content is rendered on the server, ensuring users see the page immediately.
|
|
57
|
+
- **SEO Friendly**: Fully customizable Metadata and Open Graph (OG) tags for social sharing.
|
|
58
|
+
- **Hydration**: The client takes over smoothly without re-rendering the entire tree.
|
|
59
|
+
|
|
60
|
+
### 🌍 Internationalization (i18n)
|
|
61
|
+
|
|
62
|
+
Go global with ease. Built-in support for multi-language applications allows you to switch languages dynamically without complex configuration.
|
|
63
|
+
|
|
64
|
+
### 🛠️ Environment Configuration
|
|
65
|
+
|
|
66
|
+
Manage your application environments efficiently. `lupine.web` supports loading environment variables (from `.env` files via `lupine.api`) and injecting strictly filtered configurations into the frontend.
|
package/jsx-runtime/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* JSX.Element factory used by Typescript's JSX transform
|
|
3
|
-
* @param type
|
|
4
|
-
* @param props
|
|
5
|
-
*/
|
|
6
|
-
function jsx(type, props) {
|
|
7
|
-
return { type, props };
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function Fragment(props) {
|
|
11
|
-
return { type: 'Fragment', props };
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export { jsx as jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
|
|
1
|
+
/**
|
|
2
|
+
* JSX.Element factory used by Typescript's JSX transform
|
|
3
|
+
* @param type
|
|
4
|
+
* @param props
|
|
5
|
+
*/
|
|
6
|
+
function jsx(type, props) {
|
|
7
|
+
return { type, props };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function Fragment(props) {
|
|
11
|
+
return { type: 'Fragment', props };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { jsx as jsx, jsx as jsxs, jsx as jsxDEV, Fragment };
|
package/jsx-runtime/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "jsx-runtime",
|
|
3
|
-
"amdName": "jsxRuntime",
|
|
4
|
-
"version": "1.0.0",
|
|
5
|
-
"private": true,
|
|
6
|
-
"description": "lupine.web JSX runtime",
|
|
7
|
-
"main": "index.js",
|
|
8
|
-
"module": "index.js",
|
|
9
|
-
"umd:main": "index.js",
|
|
10
|
-
"source": "index.js",
|
|
11
|
-
"types": "src/index.d.ts",
|
|
12
|
-
"license": "MIT",
|
|
13
|
-
"peerDependencies": {
|
|
14
|
-
"lupine.web": "^1.0.0"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "jsx-runtime",
|
|
3
|
+
"amdName": "jsxRuntime",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "lupine.web JSX runtime",
|
|
7
|
+
"main": "index.js",
|
|
8
|
+
"module": "index.js",
|
|
9
|
+
"umd:main": "index.js",
|
|
10
|
+
"source": "index.js",
|
|
11
|
+
"types": "src/index.d.ts",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"lupine.web": "^1.0.0"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { JSXInternal } from '../../src/jsx';
|
|
2
|
-
export { JSXInternal as JSX };
|
|
1
|
+
import { JSXInternal } from '../../src/jsx';
|
|
2
|
+
export { JSXInternal as JSX };
|
package/package.json
CHANGED
|
@@ -1,52 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "lupine.web",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"license": "MIT",
|
|
5
|
-
"author": "uuware.com",
|
|
6
|
-
"homepage": "https://github.com/uuware/lupine.js",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/uuware/lupine.js.git",
|
|
10
|
-
"directory": "packages/lupine.web"
|
|
11
|
-
},
|
|
12
|
-
"description": "lupine.web is a extremely fast, small size and lightweight frontend framework, using React TSX syntax.",
|
|
13
|
-
"main": "src/index.ts",
|
|
14
|
-
"source": "src/index.ts",
|
|
15
|
-
"types": "src/index.ts",
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": ">= 20"
|
|
18
|
-
},
|
|
19
|
-
"exports": {
|
|
20
|
-
".": {
|
|
21
|
-
"types": "./src/index.ts",
|
|
22
|
-
"browser": "./src/index.ts",
|
|
23
|
-
"umd": "./src/index.ts",
|
|
24
|
-
"import": "./src/index.ts",
|
|
25
|
-
"require": "./src/index.ts"
|
|
26
|
-
},
|
|
27
|
-
"./jsx-runtime": {
|
|
28
|
-
"types": "./jsx-runtime/src/index.d.ts",
|
|
29
|
-
"browser": "./jsx-runtime/index.js",
|
|
30
|
-
"umd": "./jsx-runtime/index.js",
|
|
31
|
-
"import": "./jsx-runtime/index.js",
|
|
32
|
-
"require": "./jsx-runtime/index.js"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"keywords": [
|
|
36
|
-
"frontend",
|
|
37
|
-
"responsive",
|
|
38
|
-
"lightweight",
|
|
39
|
-
"grid"
|
|
40
|
-
],
|
|
41
|
-
"scripts": {
|
|
42
|
-
"note": "echo 'build is not needed as the typescript code is supposed to be referred directly from other projects'",
|
|
43
|
-
"npm-publish": "npm publish --access public",
|
|
44
|
-
"build": "tsc"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "lupine.web",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"author": "uuware.com",
|
|
6
|
+
"homepage": "https://github.com/uuware/lupine.js",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/uuware/lupine.js.git",
|
|
10
|
+
"directory": "packages/lupine.web"
|
|
11
|
+
},
|
|
12
|
+
"description": "lupine.web is a extremely fast, small size and lightweight frontend framework, using React TSX syntax.",
|
|
13
|
+
"main": "src/index.ts",
|
|
14
|
+
"source": "src/index.ts",
|
|
15
|
+
"types": "src/index.ts",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">= 20"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./src/index.ts",
|
|
22
|
+
"browser": "./src/index.ts",
|
|
23
|
+
"umd": "./src/index.ts",
|
|
24
|
+
"import": "./src/index.ts",
|
|
25
|
+
"require": "./src/index.ts"
|
|
26
|
+
},
|
|
27
|
+
"./jsx-runtime": {
|
|
28
|
+
"types": "./jsx-runtime/src/index.d.ts",
|
|
29
|
+
"browser": "./jsx-runtime/index.js",
|
|
30
|
+
"umd": "./jsx-runtime/index.js",
|
|
31
|
+
"import": "./jsx-runtime/index.js",
|
|
32
|
+
"require": "./jsx-runtime/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"frontend",
|
|
37
|
+
"responsive",
|
|
38
|
+
"lightweight",
|
|
39
|
+
"grid"
|
|
40
|
+
],
|
|
41
|
+
"scripts": {
|
|
42
|
+
"note": "echo 'build is not needed as the typescript code is supposed to be referred directly from other projects'",
|
|
43
|
+
"npm-publish": "npm publish --access public",
|
|
44
|
+
"build-min": "tsc && npx esbuild ./dist/build.js --minify --outfile=./dist/build.min.js",
|
|
45
|
+
"build": "tsc"
|
|
46
|
+
},
|
|
47
|
+
"peerDependencies": {
|
|
48
|
+
"jsx-runtime": "^1.0.0"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^22.10.5"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
import { bindRef } from './bind-ref';
|
|
2
|
-
|
|
3
|
-
export const bindAttributesChildren = (topEl: Element, children: any) => {
|
|
4
|
-
for (let i = 0; i < children.length; i++) {
|
|
5
|
-
const item = children[i];
|
|
6
|
-
if (item && item.type && item.props) {
|
|
7
|
-
bindAttributes(topEl, item.type, item.props);
|
|
8
|
-
} else if (item && Array.isArray(item)) {
|
|
9
|
-
bindAttributesChildren(topEl, item);
|
|
10
|
-
} else if (
|
|
11
|
-
typeof item !== 'undefined' &&
|
|
12
|
-
item !== null &&
|
|
13
|
-
typeof item !== 'string' &&
|
|
14
|
-
typeof item !== 'number' &&
|
|
15
|
-
typeof item !== 'boolean'
|
|
16
|
-
) {
|
|
17
|
-
console.warn(`Unexpected children:`, item);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const bindAttributes = (topEl: Element, type: any, props: any) => {
|
|
23
|
-
const newProps = (props._result && props._result.props) || props;
|
|
24
|
-
if (newProps._id) {
|
|
25
|
-
let el = topEl.querySelector(`[${newProps._id}]`);
|
|
26
|
-
if (!el && topEl.getAttribute(newProps._id) === '') {
|
|
27
|
-
el = topEl;
|
|
28
|
-
}
|
|
29
|
-
if (el) {
|
|
30
|
-
for (let i in newProps) {
|
|
31
|
-
if (i === 'ref') {
|
|
32
|
-
bindRef(type, newProps, el);
|
|
33
|
-
// } else if (i === "css") {
|
|
34
|
-
// mountStyles(`[${newProps._id}]`, `[${newProps._id}]`, newProps[i]);
|
|
35
|
-
} else if (i[0] === 'o' && i[1] === 'n') {
|
|
36
|
-
let name = i;
|
|
37
|
-
if (name.toLowerCase() in el) name = name.toLowerCase().slice(2);
|
|
38
|
-
else name = name.slice(2);
|
|
39
|
-
// console.log('===bind event', name, el);
|
|
40
|
-
el.addEventListener(name, newProps[i]);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (newProps.children && Array.isArray(newProps.children)) {
|
|
47
|
-
bindAttributesChildren(topEl, newProps.children);
|
|
48
|
-
} else if (newProps._result && newProps._result.type !== 'Fragment' && newProps._result.props) {
|
|
49
|
-
bindAttributes(topEl, newProps._result.type, newProps._result.props);
|
|
50
|
-
} else if (newProps.children && newProps.children.type && newProps.children.props) {
|
|
51
|
-
bindAttributes(topEl, newProps.children.type, newProps.children.props);
|
|
52
|
-
} else if (
|
|
53
|
-
!newProps.children ||
|
|
54
|
-
typeof newProps.children === 'string' ||
|
|
55
|
-
typeof newProps.children === 'number' ||
|
|
56
|
-
typeof newProps.children === 'boolean'
|
|
57
|
-
) {
|
|
58
|
-
} else {
|
|
59
|
-
console.warn(`Unexpected children:`, newProps.children, type, props);
|
|
60
|
-
}
|
|
61
|
-
};
|
|
1
|
+
import { bindRef } from './bind-ref';
|
|
2
|
+
|
|
3
|
+
export const bindAttributesChildren = (topEl: Element, children: any) => {
|
|
4
|
+
for (let i = 0; i < children.length; i++) {
|
|
5
|
+
const item = children[i];
|
|
6
|
+
if (item && item.type && item.props) {
|
|
7
|
+
bindAttributes(topEl, item.type, item.props);
|
|
8
|
+
} else if (item && Array.isArray(item)) {
|
|
9
|
+
bindAttributesChildren(topEl, item);
|
|
10
|
+
} else if (
|
|
11
|
+
typeof item !== 'undefined' &&
|
|
12
|
+
item !== null &&
|
|
13
|
+
typeof item !== 'string' &&
|
|
14
|
+
typeof item !== 'number' &&
|
|
15
|
+
typeof item !== 'boolean'
|
|
16
|
+
) {
|
|
17
|
+
console.warn(`Unexpected children:`, item);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const bindAttributes = (topEl: Element, type: any, props: any) => {
|
|
23
|
+
const newProps = (props._result && props._result.props) || props;
|
|
24
|
+
if (newProps._id) {
|
|
25
|
+
let el = topEl.querySelector(`[${newProps._id}]`);
|
|
26
|
+
if (!el && topEl.getAttribute(newProps._id) === '') {
|
|
27
|
+
el = topEl;
|
|
28
|
+
}
|
|
29
|
+
if (el) {
|
|
30
|
+
for (let i in newProps) {
|
|
31
|
+
if (i === 'ref') {
|
|
32
|
+
bindRef(type, newProps, el);
|
|
33
|
+
// } else if (i === "css") {
|
|
34
|
+
// mountStyles(`[${newProps._id}]`, `[${newProps._id}]`, newProps[i]);
|
|
35
|
+
} else if (i[0] === 'o' && i[1] === 'n') {
|
|
36
|
+
let name = i;
|
|
37
|
+
if (name.toLowerCase() in el) name = name.toLowerCase().slice(2);
|
|
38
|
+
else name = name.slice(2);
|
|
39
|
+
// console.log('===bind event', name, el);
|
|
40
|
+
el.addEventListener(name, newProps[i]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (newProps.children && Array.isArray(newProps.children)) {
|
|
47
|
+
bindAttributesChildren(topEl, newProps.children);
|
|
48
|
+
} else if (newProps._result && newProps._result.type !== 'Fragment' && newProps._result.props) {
|
|
49
|
+
bindAttributes(topEl, newProps._result.type, newProps._result.props);
|
|
50
|
+
} else if (newProps.children && newProps.children.type && newProps.children.props) {
|
|
51
|
+
bindAttributes(topEl, newProps.children.type, newProps.children.props);
|
|
52
|
+
} else if (
|
|
53
|
+
!newProps.children ||
|
|
54
|
+
typeof newProps.children === 'string' ||
|
|
55
|
+
typeof newProps.children === 'number' ||
|
|
56
|
+
typeof newProps.children === 'boolean'
|
|
57
|
+
) {
|
|
58
|
+
} else {
|
|
59
|
+
console.warn(`Unexpected children:`, newProps.children, type, props);
|
|
60
|
+
}
|
|
61
|
+
};
|
package/src/core/bind-lang.ts
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
import { setCookie } from '../lib/cookie';
|
|
2
|
-
import { isFrontEnd } from '../lib/is-frontend';
|
|
3
|
-
import { getEitherCookie } from './server-cookie';
|
|
4
|
-
|
|
5
|
-
// The FE only loads one language for the consideration of the size
|
|
6
|
-
|
|
7
|
-
export const defaultLangName = 'en';
|
|
8
|
-
export const langCookieName = 'lang';
|
|
9
|
-
export const updateLangEventName = 'updateLang';
|
|
10
|
-
const _langCfg: any = { defaultLang: defaultLangName, langs: {} };
|
|
11
|
-
export type OneLangProps = { [key: string]: string };
|
|
12
|
-
export const bindLang = (defaultLang: string, langs: OneLangProps) => {
|
|
13
|
-
_langCfg.defaultLang = defaultLang;
|
|
14
|
-
_langCfg.langs = langs;
|
|
15
|
-
|
|
16
|
-
// set to cookie
|
|
17
|
-
getCurrentLang();
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export const getCurrentLang = () => {
|
|
21
|
-
let langName = getEitherCookie(langCookieName) as string;
|
|
22
|
-
if (!langName || !_langCfg.langs[langName]) {
|
|
23
|
-
langName = _langCfg.defaultLang;
|
|
24
|
-
if (isFrontEnd()) {
|
|
25
|
-
setCookie(langCookieName, _langCfg.defaultLang);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return { langName, langs: _langCfg.langs };
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// the FE needs to reload the page when the language is changed
|
|
32
|
-
export const updateLang = (langName: string) => {
|
|
33
|
-
// Lang is only updated in Browser
|
|
34
|
-
_langCfg.defaultLang = langName;
|
|
35
|
-
if (!isFrontEnd()) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
setCookie(langCookieName, langName);
|
|
40
|
-
// document.documentElement.setAttribute(langAttributeName, langName);
|
|
41
|
-
|
|
42
|
-
// // update lang for all iframe
|
|
43
|
-
// const allIframe = document.querySelectorAll('iframe');
|
|
44
|
-
// for (let i = 0; i < allIframe.length; i++) {
|
|
45
|
-
// if (allIframe[i].contentWindow && allIframe[i].contentWindow!.top === window) {
|
|
46
|
-
// allIframe[i].contentWindow!.document.documentElement.setAttribute(langAttributeName, langName);
|
|
47
|
-
// }
|
|
48
|
-
// }
|
|
49
|
-
|
|
50
|
-
const event = new CustomEvent(updateLangEventName, { detail: langName });
|
|
51
|
-
window.dispatchEvent(event);
|
|
52
|
-
};
|
|
1
|
+
import { setCookie } from '../lib/cookie';
|
|
2
|
+
import { isFrontEnd } from '../lib/is-frontend';
|
|
3
|
+
import { getEitherCookie } from './server-cookie';
|
|
4
|
+
|
|
5
|
+
// The FE only loads one language for the consideration of the size
|
|
6
|
+
|
|
7
|
+
export const defaultLangName = 'en';
|
|
8
|
+
export const langCookieName = 'lang';
|
|
9
|
+
export const updateLangEventName = 'updateLang';
|
|
10
|
+
const _langCfg: any = { defaultLang: defaultLangName, langs: {} };
|
|
11
|
+
export type OneLangProps = { [key: string]: string };
|
|
12
|
+
export const bindLang = (defaultLang: string, langs: OneLangProps) => {
|
|
13
|
+
_langCfg.defaultLang = defaultLang;
|
|
14
|
+
_langCfg.langs = langs;
|
|
15
|
+
|
|
16
|
+
// set to cookie
|
|
17
|
+
getCurrentLang();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export const getCurrentLang = () => {
|
|
21
|
+
let langName = getEitherCookie(langCookieName) as string;
|
|
22
|
+
if (!langName || !_langCfg.langs[langName]) {
|
|
23
|
+
langName = _langCfg.defaultLang;
|
|
24
|
+
if (isFrontEnd()) {
|
|
25
|
+
setCookie(langCookieName, _langCfg.defaultLang);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { langName, langs: _langCfg.langs };
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// the FE needs to reload the page when the language is changed
|
|
32
|
+
export const updateLang = (langName: string) => {
|
|
33
|
+
// Lang is only updated in Browser
|
|
34
|
+
_langCfg.defaultLang = langName;
|
|
35
|
+
if (!isFrontEnd()) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setCookie(langCookieName, langName);
|
|
40
|
+
// document.documentElement.setAttribute(langAttributeName, langName);
|
|
41
|
+
|
|
42
|
+
// // update lang for all iframe
|
|
43
|
+
// const allIframe = document.querySelectorAll('iframe');
|
|
44
|
+
// for (let i = 0; i < allIframe.length; i++) {
|
|
45
|
+
// if (allIframe[i].contentWindow && allIframe[i].contentWindow!.top === window) {
|
|
46
|
+
// allIframe[i].contentWindow!.document.documentElement.setAttribute(langAttributeName, langName);
|
|
47
|
+
// }
|
|
48
|
+
// }
|
|
49
|
+
|
|
50
|
+
const event = new CustomEvent(updateLangEventName, { detail: langName });
|
|
51
|
+
window.dispatchEvent(event);
|
|
52
|
+
};
|
package/src/core/bind-links.ts
CHANGED
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
import { _lupineJs } from './export-lupine';
|
|
2
|
-
|
|
3
|
-
export function bindLinks(el: Element | Document) {
|
|
4
|
-
const links = el.getElementsByTagName('a');
|
|
5
|
-
for (var i = 0, l = links.length; i < l; i++) {
|
|
6
|
-
let
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return false;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { _lupineJs } from './export-lupine';
|
|
2
|
+
|
|
3
|
+
export function bindLinks(el: Element | Document) {
|
|
4
|
+
const links = el.getElementsByTagName('a');
|
|
5
|
+
for (var i = 0, l = links.length; i < l; i++) {
|
|
6
|
+
let originalHref = links[i].getAttribute('href');
|
|
7
|
+
if (!originalHref || originalHref.startsWith('javascript:')) continue;
|
|
8
|
+
if (originalHref.startsWith('#')) {
|
|
9
|
+
links[i].onclick = () => {
|
|
10
|
+
const id = decodeURIComponent(originalHref!.substring(1));
|
|
11
|
+
document.getElementById(id)?.scrollIntoView(true);
|
|
12
|
+
return false;
|
|
13
|
+
};
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
let href = new URL(links[i].href, document.baseURI).href;
|
|
17
|
+
if (links[i].target !== '_blank' && href.startsWith(document.location.origin)) {
|
|
18
|
+
href = href.substring(document.location.origin.length);
|
|
19
|
+
// console.log(`====${href}, javascript:init('${document.links[i].href}')`);
|
|
20
|
+
links[i].onclick = () => {
|
|
21
|
+
_lupineJs.initializePage(href);
|
|
22
|
+
return false;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/core/bind-meta.tsx
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
// import { bindPageResetEvent } from '../core/page-reset-events';
|
|
2
|
-
|
|
3
|
-
let _pageTitle = { value: '', defaultValue: '' };
|
|
4
|
-
export const setPageTitle = (title: string) => {
|
|
5
|
-
_pageTitle.value = title;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
export const getPageTitle = () => {
|
|
9
|
-
return _pageTitle.value || _pageTitle.defaultValue;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export const setDefaultPageTitle = (title: string) => {
|
|
13
|
-
_pageTitle.defaultValue = title;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
let _description = { value: '', defaultValue: '' };
|
|
17
|
-
export const setMetaDescription = (description: string) => {
|
|
18
|
-
_description.value = description;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export const getMetaDescription = () => {
|
|
22
|
-
return _description.value || _description.defaultValue;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const setDefaultMetaDescription = (description: string) => {
|
|
26
|
-
_description.defaultValue = description;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
let _metaData: { [key: string]: string } = {};
|
|
30
|
-
export const addMetaDataTags = (key: string, value: string) => {
|
|
31
|
-
if (typeof value === 'undefined') {
|
|
32
|
-
delete _metaData[key];
|
|
33
|
-
} else {
|
|
34
|
-
_metaData[key] = value;
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export const getMetaDataTags = () => {
|
|
39
|
-
return Object.values(getMetaDataObject()).join('\n');
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export const getMetaDataObject = () => {
|
|
43
|
-
const metaDescription = getMetaDescription();
|
|
44
|
-
return metaDescription
|
|
45
|
-
? Object.assign(
|
|
46
|
-
{
|
|
47
|
-
'name:description': `<meta name="description" content="${metaDescription}">`,
|
|
48
|
-
},
|
|
49
|
-
_metaData
|
|
50
|
-
)
|
|
51
|
-
: _metaData;
|
|
52
|
-
};
|
|
1
|
+
// import { bindPageResetEvent } from '../core/page-reset-events';
|
|
2
|
+
|
|
3
|
+
let _pageTitle = { value: '', defaultValue: '' };
|
|
4
|
+
export const setPageTitle = (title: string) => {
|
|
5
|
+
_pageTitle.value = title;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const getPageTitle = () => {
|
|
9
|
+
return _pageTitle.value || _pageTitle.defaultValue;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const setDefaultPageTitle = (title: string) => {
|
|
13
|
+
_pageTitle.defaultValue = title;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
let _description = { value: '', defaultValue: '' };
|
|
17
|
+
export const setMetaDescription = (description: string) => {
|
|
18
|
+
_description.value = description;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getMetaDescription = () => {
|
|
22
|
+
return _description.value || _description.defaultValue;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const setDefaultMetaDescription = (description: string) => {
|
|
26
|
+
_description.defaultValue = description;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
let _metaData: { [key: string]: string } = {};
|
|
30
|
+
export const addMetaDataTags = (key: string, value: string) => {
|
|
31
|
+
if (typeof value === 'undefined') {
|
|
32
|
+
delete _metaData[key];
|
|
33
|
+
} else {
|
|
34
|
+
_metaData[key] = value;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const getMetaDataTags = () => {
|
|
39
|
+
return Object.values(getMetaDataObject()).join('\n');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const getMetaDataObject = () => {
|
|
43
|
+
const metaDescription = getMetaDescription();
|
|
44
|
+
return metaDescription
|
|
45
|
+
? Object.assign(
|
|
46
|
+
{
|
|
47
|
+
'name:description': `<meta name="description" content="${metaDescription}">`,
|
|
48
|
+
},
|
|
49
|
+
_metaData
|
|
50
|
+
)
|
|
51
|
+
: _metaData;
|
|
52
|
+
};
|