5htp-core 0.3.3 → 0.3.4-2
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/package.json +1 -1
- package/src/client/assets/css/core.less +1 -20
- package/src/client/components/Form.ts +4 -3
- package/src/client/components/Select/index.tsx +4 -1
- package/src/client/services/router/components/router.tsx +41 -39
- package/src/common/validation/validator.ts +12 -2
- package/src/common/validation/validators.ts +5 -5
- package/src/server/app/index.ts +6 -2
- package/src/server/services/console/index.ts +4 -4
- package/src/server/services/router/http/index.ts +8 -1
- package/src/types/global/modules.d.ts +6 -4
- package/tsconfig.common.json +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "5htp-core",
|
|
3
3
|
"description": "Convenient TypeScript framework designed for Performance and Productivity.",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.4-2",
|
|
5
5
|
"author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
|
|
6
6
|
"repository": "git://github.com/gaetanlegac/5htp-core.git",
|
|
7
7
|
"license": "MIT",
|
|
@@ -106,23 +106,4 @@ body {
|
|
|
106
106
|
-webkit-font-smoothing: antialiased;
|
|
107
107
|
-moz-osx-font-smoothing: grayscale;
|
|
108
108
|
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/*#loading {
|
|
112
|
-
position: fixed;
|
|
113
|
-
top: 0;
|
|
114
|
-
left: 0;
|
|
115
|
-
width: 100%;
|
|
116
|
-
height: 100%;
|
|
117
|
-
|
|
118
|
-
display: flex;
|
|
119
|
-
flex-direction: column;
|
|
120
|
-
align-items: center;
|
|
121
|
-
justify-content: center;
|
|
122
|
-
|
|
123
|
-
backdrop-filter: blur(10px);
|
|
124
|
-
background: fade(#fff, 50%);
|
|
125
|
-
color: #000;
|
|
126
|
-
|
|
127
|
-
visibility: hidden;
|
|
128
|
-
}*/
|
|
109
|
+
}
|
|
@@ -181,15 +181,16 @@ export default function useForm<TFormData extends {}>(
|
|
|
181
181
|
|
|
182
182
|
// Submit on press enter
|
|
183
183
|
onKeyDown: e => {
|
|
184
|
-
if (e.key === 'Enter') {
|
|
184
|
+
if (e.key === 'Enter' || (e.keyCode || e.which) === 13) {
|
|
185
185
|
submit({ [fieldName]: e.target.value } as Partial<TFormData>);
|
|
186
186
|
}
|
|
187
187
|
},
|
|
188
188
|
|
|
189
189
|
// Error
|
|
190
190
|
errors: state.errors[fieldName],
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
|
|
192
|
+
// Component attributes
|
|
193
|
+
...validator.componentAttributes
|
|
193
194
|
}
|
|
194
195
|
}
|
|
195
196
|
}
|
|
@@ -180,7 +180,10 @@ export default ({
|
|
|
180
180
|
</div>
|
|
181
181
|
|
|
182
182
|
{(enableSearch && choices.length !== 0 && search.keywords.length !== 0) && (
|
|
183
|
-
<ul class="row al-left wrap sp-05 pd-1"
|
|
183
|
+
<ul class="row al-left wrap sp-05 pd-1" style={{
|
|
184
|
+
maxHeight: '30vh',
|
|
185
|
+
overflowY: 'auto'
|
|
186
|
+
}}>
|
|
184
187
|
{choices.map( choice => (
|
|
185
188
|
<ChoiceElement choice={choice}
|
|
186
189
|
currentList={currentList}
|
|
@@ -8,7 +8,7 @@ import React from 'react';
|
|
|
8
8
|
import useContext from '@/client/context';
|
|
9
9
|
|
|
10
10
|
// Specific
|
|
11
|
-
import type
|
|
11
|
+
import type ClientRouter from '..';
|
|
12
12
|
import PageComponent from './Page';
|
|
13
13
|
import ClientRequest from '../request';
|
|
14
14
|
import { history, location, Update } from '../request/history';
|
|
@@ -29,35 +29,47 @@ export type PropsPage<TParams extends { [cle: string]: unknown }> = TParams & {
|
|
|
29
29
|
|
|
30
30
|
const LogPrefix = `[router][component]`
|
|
31
31
|
|
|
32
|
+
const PageLoading = ({ clientRouter }: { clientRouter?: ClientRouter }) => {
|
|
33
|
+
|
|
34
|
+
const [isLoading, setLoading] = React.useState(false);
|
|
35
|
+
|
|
36
|
+
if (clientRouter)
|
|
37
|
+
clientRouter.setLoading = setLoading;
|
|
38
|
+
|
|
39
|
+
return isLoading ? (
|
|
40
|
+
<div id="loading">
|
|
41
|
+
<i src="spin" />
|
|
42
|
+
</div>
|
|
43
|
+
) : null
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
|
|
32
47
|
/*----------------------------------
|
|
33
48
|
- COMPONENT
|
|
34
49
|
----------------------------------*/
|
|
35
|
-
export default ({ service:
|
|
50
|
+
export default ({ service: clientRouter }: { service?: ClientRouter }) => {
|
|
36
51
|
|
|
37
52
|
const context = useContext();
|
|
38
53
|
|
|
39
54
|
const [pages, setPages] = React.useState<{
|
|
40
|
-
current: undefined | Page
|
|
41
|
-
loading: boolean
|
|
55
|
+
current: undefined | Page
|
|
42
56
|
}>({
|
|
43
|
-
current: context.page
|
|
44
|
-
loading: false
|
|
57
|
+
current: context.page
|
|
45
58
|
});
|
|
46
59
|
|
|
47
60
|
const resolvePage = async (request: ClientRequest, locationUpdate?: Update) => {
|
|
48
61
|
|
|
62
|
+
if (!clientRouter) return;
|
|
63
|
+
|
|
49
64
|
// WARNING: Don"t try to play with pages here, since the object will not be updated
|
|
50
65
|
// If needed to play with pages, do it in the setPages callback below
|
|
51
66
|
|
|
52
|
-
// Set
|
|
53
|
-
|
|
54
|
-
...oldState,
|
|
55
|
-
loading: true,
|
|
56
|
-
}));
|
|
67
|
+
// Set loading state
|
|
68
|
+
clientRouter.setLoading(true);
|
|
57
69
|
|
|
58
70
|
// Load the route chunks
|
|
59
71
|
context.request = request;
|
|
60
|
-
const newpage = context.page = await
|
|
72
|
+
const newpage = context.page = await clientRouter.resolve(request);
|
|
61
73
|
|
|
62
74
|
// Page not found: Directly load with the browser
|
|
63
75
|
if (newpage === undefined) {
|
|
@@ -69,17 +81,18 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
69
81
|
return;
|
|
70
82
|
}
|
|
71
83
|
|
|
72
|
-
|
|
84
|
+
// Fetch API data to hydrate the page
|
|
85
|
+
const newData = context.data = await newpage.fetchData();
|
|
73
86
|
|
|
74
87
|
// Add page container
|
|
75
88
|
setPages( pages => {
|
|
76
89
|
|
|
77
|
-
const currentRoute = pages.current?.route;
|
|
78
|
-
|
|
79
90
|
// Check if the page changed
|
|
80
|
-
if (
|
|
91
|
+
if (pages.current?.chunkId === newpage.chunkId) {
|
|
81
92
|
console.warn(LogPrefix, "Canceling navigation to the same page:", {...request});
|
|
82
|
-
|
|
93
|
+
pages.current.setAllData(newData);
|
|
94
|
+
clientRouter.setLoading(false);
|
|
95
|
+
return { ...pages }
|
|
83
96
|
}
|
|
84
97
|
|
|
85
98
|
// If if the layout changed
|
|
@@ -93,24 +106,12 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
93
106
|
// Find a way to unload the previous layout / page resources before to load the new one
|
|
94
107
|
console.log(LogPrefix, `Changing layout. Before:`, curLayout, 'New layout:', newLayout);
|
|
95
108
|
window.location.replace(request.url);
|
|
96
|
-
return { ...pages
|
|
109
|
+
return { ...pages }
|
|
97
110
|
|
|
98
111
|
context.app.setLayout(newLayout);
|
|
99
112
|
}
|
|
100
113
|
|
|
101
|
-
|
|
102
|
-
const oldPage = pages.current;
|
|
103
|
-
if (oldPage !== undefined) {
|
|
104
|
-
setTimeout(() => setPages({
|
|
105
|
-
current: newpage,
|
|
106
|
-
loading: false
|
|
107
|
-
}), 500);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
current: newpage,
|
|
112
|
-
loading: false
|
|
113
|
-
}
|
|
114
|
+
return { current: newpage }
|
|
114
115
|
});
|
|
115
116
|
}
|
|
116
117
|
|
|
@@ -136,13 +137,18 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
136
137
|
await resolvePage(request);
|
|
137
138
|
|
|
138
139
|
// Scroll to the selected content via url hash
|
|
139
|
-
restoreScroll(pages.current);
|
|
140
|
+
//restoreScroll(pages.current);
|
|
140
141
|
})
|
|
141
142
|
}, []);
|
|
142
143
|
|
|
143
144
|
// On every page change
|
|
144
145
|
React.useEffect(() => {
|
|
145
146
|
|
|
147
|
+
if (!clientRouter) return;
|
|
148
|
+
|
|
149
|
+
// Page loaded
|
|
150
|
+
clientRouter.setLoading(false);
|
|
151
|
+
|
|
146
152
|
// Reset scroll
|
|
147
153
|
window.scrollTo(0, 0);
|
|
148
154
|
// Should be called AFTER rendering the page (so after the state change)
|
|
@@ -151,10 +157,10 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
151
157
|
restoreScroll(pages.current);
|
|
152
158
|
|
|
153
159
|
// Hooks
|
|
154
|
-
|
|
160
|
+
clientRouter.runHook('page.changed', pages.current)
|
|
155
161
|
|
|
156
162
|
}, [pages.current]);
|
|
157
|
-
|
|
163
|
+
|
|
158
164
|
// Render the page component
|
|
159
165
|
return <>
|
|
160
166
|
{/*pages.previous && (
|
|
@@ -169,10 +175,6 @@ export default ({ service: router }: { service: Router }) => {
|
|
|
169
175
|
/>
|
|
170
176
|
)}
|
|
171
177
|
|
|
172
|
-
{
|
|
173
|
-
<div id="loading">
|
|
174
|
-
<i src="spin" />
|
|
175
|
-
</div>
|
|
176
|
-
)}
|
|
178
|
+
<PageLoading clientRouter={clientRouter} />
|
|
177
179
|
</>
|
|
178
180
|
}
|
|
@@ -10,6 +10,7 @@ import { InputError } from '@common/errors';
|
|
|
10
10
|
|
|
11
11
|
// Specific
|
|
12
12
|
import type { TValidateOptions } from './schema';
|
|
13
|
+
import type { InputBaseProps } from '@client/components/inputv3/base';
|
|
13
14
|
|
|
14
15
|
/*----------------------------------
|
|
15
16
|
- TYPES
|
|
@@ -64,14 +65,23 @@ export const EXCLUDE_VALUE = "action:exclure" as const;
|
|
|
64
65
|
/*----------------------------------
|
|
65
66
|
- CLASS
|
|
66
67
|
----------------------------------*/
|
|
67
|
-
export default class Validator<
|
|
68
|
+
export default class Validator<
|
|
69
|
+
TValue,
|
|
70
|
+
TOptions extends TValidator<TValue> = TValidator<TValue>,
|
|
71
|
+
TComponent = React.FunctionComponent< InputBaseProps< TValue > >
|
|
72
|
+
> {
|
|
68
73
|
|
|
69
74
|
public constructor(
|
|
70
75
|
public type: string,
|
|
71
76
|
public validateType: TValidationFunction<TValue>,
|
|
72
|
-
public options: TOptions
|
|
77
|
+
public options: TOptions,
|
|
78
|
+
public componentAttributes: Partial<InputBaseProps<TValue>> = {}
|
|
73
79
|
) {
|
|
74
80
|
|
|
81
|
+
// Basic component attriutes
|
|
82
|
+
this.componentAttributes.required = options?.opt !== true;
|
|
83
|
+
//this.componentAttributes.validator = this;
|
|
84
|
+
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
public isEmpty = (val: any) => val === undefined || val === '' || val === null
|
|
@@ -111,22 +111,22 @@ export default class SchemaValidators {
|
|
|
111
111
|
})
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
public choice = (
|
|
114
|
+
public choice = (choices?: any[], opts: TValidator<any> & {} = {}) =>
|
|
115
115
|
new Validator<any>('choice', (val, input, output) => {
|
|
116
116
|
|
|
117
117
|
// Choice object
|
|
118
118
|
if (typeof val === 'object' && ('value' in val) && typeof val.value !== 'object')
|
|
119
119
|
val = val.value;
|
|
120
120
|
|
|
121
|
-
if (
|
|
122
|
-
const isValid =
|
|
121
|
+
if (choices !== undefined) {
|
|
122
|
+
const isValid = choices.some(v => v.value === val);
|
|
123
123
|
if (!isValid)
|
|
124
|
-
throw new InputError("Invalid value. Must be: " +
|
|
124
|
+
throw new InputError("Invalid value. Must be: " + choices.map(v => v.value).join(', '));
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
return val;
|
|
128
128
|
|
|
129
|
-
}, opts)
|
|
129
|
+
}, opts, { choices })
|
|
130
130
|
|
|
131
131
|
/*----------------------------------
|
|
132
132
|
- CHAINES
|
package/src/server/app/index.ts
CHANGED
|
@@ -124,19 +124,23 @@ export class Application<
|
|
|
124
124
|
|
|
125
125
|
protected async start() {
|
|
126
126
|
|
|
127
|
+
console.log("Build date", BUILD_DATE);
|
|
128
|
+
console.log("Core version", CORE_VERSION);
|
|
127
129
|
console.log(`5HTP Core`, process.env.npm_package_version);
|
|
130
|
+
const startTime = Date.now();
|
|
128
131
|
|
|
129
132
|
// Handle errors & crashs
|
|
130
133
|
this.on('error', e => this.Console.createBugReport(e))
|
|
131
134
|
|
|
132
135
|
console.info(`[boot] Start services`);
|
|
133
136
|
await this.startServices();
|
|
137
|
+
this.debug && console.info(`[boot] Services are ready`);
|
|
134
138
|
|
|
135
|
-
this.debug && console.info(`[boot] App ready`);
|
|
136
139
|
await this.ready();
|
|
137
140
|
await this.runHook('ready');
|
|
138
141
|
|
|
139
|
-
|
|
142
|
+
const startedTime = (Date.now() - startTime) / 1000;
|
|
143
|
+
console.info(`[boot] Application launched in ${startedTime}s`);
|
|
140
144
|
this.launched = true;
|
|
141
145
|
}
|
|
142
146
|
|
|
@@ -167,7 +167,7 @@ export default class Console extends Service<Config, Hooks, Application, Service
|
|
|
167
167
|
origLog(logMetaMarkup + formatWithOptions(settings.prettyInspectOptions, ...logArgs) + logErrorsStr);
|
|
168
168
|
},
|
|
169
169
|
}
|
|
170
|
-
});
|
|
170
|
+
});
|
|
171
171
|
|
|
172
172
|
if (console["_wrapped"] !== undefined)
|
|
173
173
|
return;
|
|
@@ -176,7 +176,7 @@ export default class Console extends Service<Config, Hooks, Application, Service
|
|
|
176
176
|
console[ logLevel ] = (...args: any[]) => {
|
|
177
177
|
|
|
178
178
|
// Dev mode = no care about performance = rich logging
|
|
179
|
-
if (this.app.env.profile === 'dev')
|
|
179
|
+
if (this.app.env.profile === 'dev' || ['warn', 'error'].includes( logLevel ))
|
|
180
180
|
//this.logger[ logLevel ](...args);
|
|
181
181
|
origLog(...args);
|
|
182
182
|
// Prod mode = minimal logging
|
|
@@ -350,10 +350,10 @@ export default class Console extends Service<Config, Hooks, Application, Service
|
|
|
350
350
|
|
|
351
351
|
public printHtml( logs: TJsonLog[], full: boolean = false ): string {
|
|
352
352
|
|
|
353
|
-
let html = logs.map( logEntry => logToHTML( logEntry, this )).join('
|
|
353
|
+
let html = logs.map( logEntry => logToHTML( logEntry, this )).join('<br />');
|
|
354
354
|
|
|
355
355
|
if (full) {
|
|
356
|
-
const consoleCss = `background: #000; padding: 20px; font-family: 'Fira Mono', 'monospace', 'Monaco'; font-size: 12px; line-height: 20px;`
|
|
356
|
+
const consoleCss = `background: #000; padding: 20px; font-family: 'Fira Mono', 'monospace', 'Monaco'; font-size: 12px; line-height: 20px;color: #aaa;`
|
|
357
357
|
html = '<div style="' + consoleCss + '">' + html + '</div>';
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -121,8 +121,15 @@ export default class HttpServer {
|
|
|
121
121
|
serveStatic: {
|
|
122
122
|
setHeaders: function setCustomCacheControl(res, path) {
|
|
123
123
|
|
|
124
|
+
const dontCache = [
|
|
125
|
+
'/public/icons',
|
|
126
|
+
'/public/client'
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
res.setHeader('Cache-Control', 'public, max-age=0');
|
|
130
|
+
|
|
124
131
|
// Set long term cache, except for non-hashed filenames
|
|
125
|
-
/*if (
|
|
132
|
+
/*if (dontCache.some( p => path.startsWith( p ))) {
|
|
126
133
|
res.setHeader('Cache-Control', 'public, max-age=0');
|
|
127
134
|
} else {
|
|
128
135
|
res.setHeader('Cache-Control', 'public, max-age=604800000'); // 7 Days
|
|
@@ -36,10 +36,12 @@ declare module "*.svg" {
|
|
|
36
36
|
export = value;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
declare module "*.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
declare module "*.png";
|
|
40
|
+
declare module "*.jpg";
|
|
41
|
+
declare module "*.jpeg";
|
|
42
|
+
declare module "*.webp";
|
|
43
|
+
declare module "*.gif";
|
|
44
|
+
declare module "*.bmp";
|
|
43
45
|
|
|
44
46
|
declare module "*.mp3" {
|
|
45
47
|
const value: string;
|
package/tsconfig.common.json
CHANGED