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 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.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
- required: validator.options?.opt !== true,
192
- validator: validator,
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 Router from '..';
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: router }: { service: Router }) => {
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.loading state
53
- setPages( oldState => ({
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 router.resolve(request);
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
- const data = context.data = await newpage.fetchData();
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 (currentRoute?.path === request.path) {
91
+ if (pages.current?.chunkId === newpage.chunkId) {
81
92
  console.warn(LogPrefix, "Canceling navigation to the same page:", {...request});
82
- return { ...pages, loading: false }
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, loading: false }
109
+ return { ...pages }
97
110
 
98
111
  context.app.setLayout(newLayout);
99
112
  }
100
113
 
101
- // Remove old page after the aff-page css transition
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
- router.runHook('page.changed', pages.current)
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
- {pages.loading && (
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<TValue, TOptions extends TValidator<TValue> = TValidator<TValue>> {
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 = (values?: any[], opts: TValidator<any> & {} = {}) =>
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 (values !== undefined) {
122
- const isValid = values.some(v => v.value === val);
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: " + values.map(v => v.value).join(', '));
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
@@ -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
- this.debug && console.info(`[boot] Application is ready.`);
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('\n');
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 (__DEV__ || path.includes('/icons.')) {
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 "*.(png|webp|jpg|jpeg|gif|bmp)" {
40
- const value: string;
41
- export = value;
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;
@@ -40,5 +40,5 @@
40
40
  "react/jsx-runtime": ["preact/jsx-runtime"]
41
41
  },
42
42
  },
43
- "include": ["src"]
43
+ "include": ["src", "types/global"]
44
44
  }