lupine.web 1.0.14
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/LICENSE +21 -0
- package/README.md +3 -0
- package/jsx-runtime/index.js +14 -0
- package/jsx-runtime/package.json +16 -0
- package/jsx-runtime/src/index.d.ts +2 -0
- package/package.json +51 -0
- package/src/assets/themes/base-themes.ts +16 -0
- package/src/assets/themes/dark-themes.ts +85 -0
- package/src/assets/themes/index.ts +4 -0
- package/src/assets/themes/light-themes.ts +92 -0
- package/src/assets/themes/shared-themes.ts +50 -0
- package/src/components/button-push-animation.tsx +138 -0
- package/src/components/button.tsx +55 -0
- package/src/components/drag-refresh.tsx +110 -0
- package/src/components/editable-label.tsx +83 -0
- package/src/components/float-window.tsx +226 -0
- package/src/components/grid.tsx +18 -0
- package/src/components/html-var.tsx +41 -0
- package/src/components/index.ts +36 -0
- package/src/components/input-with-title.tsx +24 -0
- package/src/components/link-item.tsx +13 -0
- package/src/components/link-list.tsx +62 -0
- package/src/components/menu-bar.tsx +220 -0
- package/src/components/menu-item-props.tsx +10 -0
- package/src/components/menu-sidebar.tsx +289 -0
- package/src/components/message-box.tsx +44 -0
- package/src/components/meta-data.tsx +54 -0
- package/src/components/meta-description.tsx +19 -0
- package/src/components/meta-title.tsx +19 -0
- package/src/components/modal.tsx +29 -0
- package/src/components/notice-message.tsx +119 -0
- package/src/components/paging-link.tsx +100 -0
- package/src/components/panel.tsx +24 -0
- package/src/components/popup-menu.tsx +218 -0
- package/src/components/progress.tsx +91 -0
- package/src/components/redirect.tsx +19 -0
- package/src/components/resizable-splitter.tsx +129 -0
- package/src/components/select-with-title.tsx +37 -0
- package/src/components/spinner.tsx +100 -0
- package/src/components/svg.tsx +24 -0
- package/src/components/tabs.tsx +252 -0
- package/src/components/text-glow.tsx +36 -0
- package/src/components/text-wave.tsx +54 -0
- package/src/components/theme-selector.tsx +35 -0
- package/src/components/toggle-base.tsx +260 -0
- package/src/components/toggle-switch.tsx +156 -0
- package/src/core/bind-attributes.ts +58 -0
- package/src/core/bind-lang.ts +51 -0
- package/src/core/bind-links.ts +16 -0
- package/src/core/bind-ref.ts +33 -0
- package/src/core/bind-styles.ts +180 -0
- package/src/core/bind-theme.ts +51 -0
- package/src/core/camel-to-hyphens.ts +3 -0
- package/src/core/core.ts +179 -0
- package/src/core/index.ts +15 -0
- package/src/core/mount-components.ts +259 -0
- package/src/core/page-loaded-events.ts +16 -0
- package/src/core/page-router.ts +170 -0
- package/src/core/replace-innerhtml.ts +10 -0
- package/src/core/server-cookie.ts +22 -0
- package/src/core/web-version.ts +12 -0
- package/src/global.d.ts +66 -0
- package/src/index.ts +15 -0
- package/src/jsx.ts +1041 -0
- package/src/lib/date-utils.ts +317 -0
- package/src/lib/debug-watch.ts +31 -0
- package/src/lib/deep-merge.ts +37 -0
- package/src/lib/document-ready.ts +36 -0
- package/src/lib/dom/calculate-text-width.ts +13 -0
- package/src/lib/dom/cookie.ts +41 -0
- package/src/lib/dom/download-stream.ts +17 -0
- package/src/lib/dom/download.ts +12 -0
- package/src/lib/dom/index.ts +71 -0
- package/src/lib/dynamical-load.ts +138 -0
- package/src/lib/format-bytes.ts +11 -0
- package/src/lib/index.ts +17 -0
- package/src/lib/lite-dom.ts +227 -0
- package/src/lib/logger.ts +55 -0
- package/src/lib/message-hub.ts +105 -0
- package/src/lib/observable.ts +188 -0
- package/src/lib/promise-timeout.ts +1 -0
- package/src/lib/simple-storage.ts +40 -0
- package/src/lib/stop-propagation.ts +7 -0
- package/src/lib/unique-id.ts +39 -0
- package/src/lib/upload-file.ts +68 -0
- package/src/lib/web-env.ts +98 -0
- package/src/models/index.ts +2 -0
- package/src/models/simple-storage-props.ts +9 -0
- package/src/models/to-client-delivery-props.ts +8 -0
- package/src/types/css-styles.ts +814 -0
- package/src/types/css-types.ts +17 -0
- package/src/types/index.ts +6 -0
- package/src/types/media-query.ts +93 -0
- package/tsconfig.json +113 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// some events need to be done before the page is rendered
|
|
2
|
+
const _pageLoadedEvents: Function[] = [];
|
|
3
|
+
export const callPageLoadedEvent = () => {
|
|
4
|
+
_pageLoadedEvents.forEach((i) => {
|
|
5
|
+
try {
|
|
6
|
+
i();
|
|
7
|
+
} catch (e) {
|
|
8
|
+
console.error(e);
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// The fn is called when the page is re-rendered
|
|
14
|
+
export const bindPageLoadedEvent = (fn: Function) => {
|
|
15
|
+
_pageLoadedEvents.push(fn);
|
|
16
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { VNode } from '../jsx';
|
|
2
|
+
import { Logger } from '../lib';
|
|
3
|
+
import { isFrontEnd, PageProps } from './core';
|
|
4
|
+
import { mountComponents } from './mount-components';
|
|
5
|
+
|
|
6
|
+
export type PageRouterCallback = (props: PageProps) => Promise<VNode<any> | null>;
|
|
7
|
+
|
|
8
|
+
export type PageRouterData = {
|
|
9
|
+
path: string;
|
|
10
|
+
handler: (PageRouterCallback | PageRouter)[];
|
|
11
|
+
parameterVariables: string[];
|
|
12
|
+
parameterLength: number;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type FramePageProps = {
|
|
16
|
+
component: (placeholderClassname: string, vnode: VNode<any>) => Promise<VNode<any>>;
|
|
17
|
+
placeholderClassname: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export class PageRouter {
|
|
21
|
+
logger = new Logger('page-router');
|
|
22
|
+
private routerData: PageRouterData[] = [];
|
|
23
|
+
private filter: PageRouterCallback | undefined;
|
|
24
|
+
private framePage: FramePageProps | undefined;
|
|
25
|
+
|
|
26
|
+
// if the filter returns null (passed filter), the router will continue.
|
|
27
|
+
// it works in the same way as in use method
|
|
28
|
+
setFilter(filter: PageRouterCallback) {
|
|
29
|
+
this.filter = filter;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
setFramePage(framePage: FramePageProps) {
|
|
33
|
+
this.framePage = framePage;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// the path should start with / and end without /, and it can be
|
|
37
|
+
// /aaa/:bbb/ccc/:ddd (ccc is a fixed section)
|
|
38
|
+
// /aaa/:bbb/ccc/?ddd/?eee (from ddd, all sections are optional)
|
|
39
|
+
// /aaa/:?bbb/ccc/ (from bbb, all sections are optional)
|
|
40
|
+
private storeRouter(path: string, handler: (PageRouterCallback | PageRouter)[]) {
|
|
41
|
+
let fixedPath;
|
|
42
|
+
if (path === '*' || path === '' || path === '/*') {
|
|
43
|
+
// removed path === '/' ||
|
|
44
|
+
fixedPath = '*';
|
|
45
|
+
} else {
|
|
46
|
+
fixedPath = path;
|
|
47
|
+
if (!fixedPath.startsWith('/')) {
|
|
48
|
+
fixedPath = '/' + fixedPath;
|
|
49
|
+
}
|
|
50
|
+
if (fixedPath.endsWith('/') && fixedPath.length > 1) {
|
|
51
|
+
fixedPath = fixedPath.substring(0, fixedPath.length - 1);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let parameterLength = 0;
|
|
56
|
+
let parameterVariables: string[] = [];
|
|
57
|
+
const ind = fixedPath.indexOf('/:');
|
|
58
|
+
if (ind >= 0) {
|
|
59
|
+
parameterVariables = fixedPath.substring(ind + 1).split('/');
|
|
60
|
+
fixedPath = fixedPath.substring(0, ind);
|
|
61
|
+
// from optionInd, all will be optional
|
|
62
|
+
const optionInd = parameterVariables.findIndex((item) => item.startsWith('?'));
|
|
63
|
+
parameterLength = optionInd >= 0 ? optionInd : parameterVariables.length;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
this.routerData.push({
|
|
67
|
+
path: fixedPath,
|
|
68
|
+
handler,
|
|
69
|
+
parameterVariables,
|
|
70
|
+
parameterLength,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
use(path: string, ...handler: (PageRouterCallback | PageRouter)[]) {
|
|
75
|
+
this.storeRouter(path, handler);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private async callHandle(handle: PageRouterCallback, path: string, props: PageProps): Promise<VNode<any> | null> {
|
|
79
|
+
try {
|
|
80
|
+
const vNode = await handle(props);
|
|
81
|
+
// logger.debug(`Processed path: ${path}`);
|
|
82
|
+
return vNode;
|
|
83
|
+
} catch (e: any) {
|
|
84
|
+
this.logger.error(`Processed path: ${path}, error: ${e.message}`);
|
|
85
|
+
// res.write(JSON.stringify({ status: 'error', message: `Processed path: ${path}, error: ${e.message}` }));
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async findRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
|
|
91
|
+
for (let i = 0, routerList; (routerList = this.routerData[i]); i++) {
|
|
92
|
+
if (routerList.path === '*' || url === routerList.path || url.startsWith(routerList.path + '/')) {
|
|
93
|
+
const parameters: { [key: string]: string } = {};
|
|
94
|
+
let meet = true;
|
|
95
|
+
if (routerList.parameterVariables.length > 0) {
|
|
96
|
+
meet = false;
|
|
97
|
+
let newUrl = url.substring(routerList.path.length + 1);
|
|
98
|
+
if (newUrl.endsWith('/')) {
|
|
99
|
+
newUrl = newUrl.substring(0, newUrl.length - 1);
|
|
100
|
+
}
|
|
101
|
+
const restPath = newUrl.split('/');
|
|
102
|
+
// the path must have mandatory parameters but some parameters can be optional
|
|
103
|
+
if (
|
|
104
|
+
restPath.length >= routerList.parameterLength &&
|
|
105
|
+
restPath.length <= routerList.parameterVariables.length
|
|
106
|
+
) {
|
|
107
|
+
meet = true;
|
|
108
|
+
for (const [index, item] of routerList.parameterVariables.entries()) {
|
|
109
|
+
if (!item.startsWith(':') && !item.startsWith('?') && item !== restPath[index]) {
|
|
110
|
+
meet = false;
|
|
111
|
+
break;
|
|
112
|
+
} else if ((item.startsWith(':') || item.startsWith('?')) && index < restPath.length) {
|
|
113
|
+
parameters[item.replace(/[:?]/g, '')] = restPath[index];
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
props.urlParameters = parameters;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (meet) {
|
|
121
|
+
for (let j = 0, router; (router = routerList.handler[j]); j++) {
|
|
122
|
+
if (router instanceof PageRouter) {
|
|
123
|
+
// it's a sub-level router
|
|
124
|
+
const nextPath =
|
|
125
|
+
routerList.path === '*' || (url === '/' && routerList.path === '/')
|
|
126
|
+
? url
|
|
127
|
+
: url.substring(routerList.path.length);
|
|
128
|
+
// TODO: sub-level?
|
|
129
|
+
const vNode = await router.handleRoute(nextPath, props, renderPartPage);
|
|
130
|
+
if (vNode) {
|
|
131
|
+
return vNode;
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
// it should be a function
|
|
135
|
+
// the query's url should match the api's path
|
|
136
|
+
const dom = await this.callHandle(router, url, props);
|
|
137
|
+
if (dom) {
|
|
138
|
+
return dom;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// stop process for this path if no page is found
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async handleRoute(url: string, props: PageProps, renderPartPage: boolean): Promise<VNode<any> | null> {
|
|
151
|
+
let vNode = null;
|
|
152
|
+
if (this.filter) {
|
|
153
|
+
vNode = await this.callHandle(this.filter, url, props);
|
|
154
|
+
}
|
|
155
|
+
if (!vNode) {
|
|
156
|
+
vNode = await this.findRoute(url, props, renderPartPage);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (vNode && this.framePage) {
|
|
160
|
+
const selector = '.' + this.framePage.placeholderClassname;
|
|
161
|
+
if (renderPartPage && isFrontEnd() && document.querySelector(selector)) {
|
|
162
|
+
await mountComponents(selector, vNode);
|
|
163
|
+
return null;
|
|
164
|
+
} else {
|
|
165
|
+
return this.framePage.component(this.framePage.placeholderClassname, vNode);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return vNode;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const replaceInnerhtml = async (el: Element, newHtml: string) => {
|
|
2
|
+
const promises: Promise<void>[] = [];
|
|
3
|
+
el.querySelectorAll('[data-ref]').forEach((child: any) => {
|
|
4
|
+
if (child._lj && child._lj.onUnload) {
|
|
5
|
+
promises.push(child._lj.onUnload());
|
|
6
|
+
}
|
|
7
|
+
});
|
|
8
|
+
await Promise.all(promises);
|
|
9
|
+
el.innerHTML = newHtml;
|
|
10
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { DomUtils } from '../lib';
|
|
2
|
+
import { ISimpleStorage } from '../models/simple-storage-props';
|
|
3
|
+
|
|
4
|
+
// getEitherCookie can be used in both FE and SSR
|
|
5
|
+
export const getEitherCookie = (name: string) => {
|
|
6
|
+
if (typeof window === 'undefined') {
|
|
7
|
+
// SSR
|
|
8
|
+
return getServerCookie(name);
|
|
9
|
+
} else {
|
|
10
|
+
return DomUtils.getCookie(name);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// In SSR (server-side-rendering), some components may need to access cookies
|
|
15
|
+
let _serverCookies: ISimpleStorage;
|
|
16
|
+
export const getServerCookie = (name: string) => {
|
|
17
|
+
return _serverCookies && _serverCookies.get(name, '');
|
|
18
|
+
};
|
|
19
|
+
// TODO: Server cookies safety should be OK? as this is dropped after SSR
|
|
20
|
+
export const initServerCookies = (serverCookies: ISimpleStorage) => {
|
|
21
|
+
return (_serverCookies = serverCookies);
|
|
22
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// This is Lupine.Js's version to help release confirmation, and user's api can add sub version to this.
|
|
2
|
+
const WEB_VERSION = '1.0.0 - build 20250409';
|
|
3
|
+
const _saved = {
|
|
4
|
+
WEB_VERSION,
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const addWebVersion = (version: string) => {
|
|
8
|
+
_saved.WEB_VERSION = WEB_VERSION + ` (${version})`;
|
|
9
|
+
};
|
|
10
|
+
export const getWebVersion = () => {
|
|
11
|
+
return _saved.WEB_VERSION;
|
|
12
|
+
};
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
// // declare module "*.svg";
|
|
4
|
+
// declare module "*.svg" {
|
|
5
|
+
// /**
|
|
6
|
+
// * Use `any` to avoid conflicts with
|
|
7
|
+
// * `@svgr/webpack` plugin or
|
|
8
|
+
// * `babel-plugin-inline-react-svg` plugin.
|
|
9
|
+
// */
|
|
10
|
+
// const content: any;
|
|
11
|
+
// export default content;
|
|
12
|
+
// }
|
|
13
|
+
declare module '*.svg' {
|
|
14
|
+
const src: string;
|
|
15
|
+
export default src;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
declare module '*.bmp' {
|
|
19
|
+
const src: string;
|
|
20
|
+
export default src;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare module '*.gif' {
|
|
24
|
+
const src: string;
|
|
25
|
+
export default src;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module '*.jpg' {
|
|
29
|
+
const src: string;
|
|
30
|
+
export default src;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
declare module '*.jpeg' {
|
|
34
|
+
const src: string;
|
|
35
|
+
export default src;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
declare module '*.png' {
|
|
39
|
+
const src: string;
|
|
40
|
+
export default src;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare module '*.webp' {
|
|
44
|
+
const src: string;
|
|
45
|
+
export default src;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
declare module '*.ico' {
|
|
49
|
+
const src: string;
|
|
50
|
+
export default src;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare module '*.avif' {
|
|
54
|
+
const src: string;
|
|
55
|
+
export default src;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare module '*.css' {
|
|
59
|
+
const content: { [className: string]: string };
|
|
60
|
+
export default content;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
declare module '*.module.css' {
|
|
64
|
+
const classes: { readonly [key: string]: string };
|
|
65
|
+
export default classes;
|
|
66
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="./global" />
|
|
2
|
+
|
|
3
|
+
import { JSXInternal } from './jsx';
|
|
4
|
+
export * from './jsx';
|
|
5
|
+
export * from './core';
|
|
6
|
+
export * from './lib';
|
|
7
|
+
export * from './components';
|
|
8
|
+
export * from './types';
|
|
9
|
+
export * from './assets/themes';
|
|
10
|
+
|
|
11
|
+
declare global {
|
|
12
|
+
namespace JSX {
|
|
13
|
+
interface IntrinsicElements extends JSXInternal.IntrinsicElements {}
|
|
14
|
+
}
|
|
15
|
+
}
|