5htp-core 0.4.5 → 0.4.6

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.4.5",
4
+ "version": "0.4.6",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  //transition: all .1s linear;
42
42
 
43
43
  > i {
44
- color: var(--cAccent)
44
+ color: var(--cTxtAccent)
45
45
  }
46
46
  }
47
47
 
@@ -352,7 +352,8 @@ ul.col {
352
352
  top: @sizeComponent;
353
353
  margin-top: @spacing / 2;
354
354
 
355
- &::before {
355
+ // C'est quoi ?
356
+ /*&::before {
356
357
  content: ' ';
357
358
  display: block;
358
359
  position: absolute;
@@ -362,7 +363,7 @@ ul.col {
362
363
  right: calc(0em - @spacing);
363
364
 
364
365
  height: 100%;
365
- }
366
+ }*/
366
367
 
367
368
  }
368
369
  }
@@ -2,7 +2,7 @@
2
2
  - CONFIG
3
3
  ----------------------------------*/
4
4
 
5
- @sizeStep: 2.2em;
5
+ @sizeStep: 1.8em;
6
6
  @itemLineHeight: 1.5em;
7
7
 
8
8
  /*----------------------------------
@@ -22,8 +22,10 @@ ul, ol {
22
22
 
23
23
  ul.liste,
24
24
  ol.steps {
25
- // Doit hériter des parents
26
- //line-height: 1.5em;
25
+
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 0.5em;
27
29
  text-align: left;
28
30
 
29
31
  > li {
@@ -48,7 +50,7 @@ ul.liste {
48
50
  &:before {
49
51
  content: '➜';
50
52
  font-size: 1.4em;
51
- color: var(--cAccent);
53
+ color: var(--cTxtAccent);
52
54
  }
53
55
  }
54
56
 
@@ -87,17 +89,12 @@ ol.steps {
87
89
 
88
90
  &:before {
89
91
  content: counter(step-counter);
90
- font-size: 0.8em;
91
- }
92
-
93
- &.active::before {
94
- background-color: var(--cAccent);
95
92
  }
96
93
 
97
94
  // Stepnumber = at the left
98
95
  &:not(.col) {
99
96
 
100
- padding-left: @sizeStep + (@spacing / 2);
97
+ padding-left: /*@sizeStep + */(@spacing);
101
98
  padding-top: (@sizeStep - @itemLineHeight) / 2;
102
99
  line-height: @itemLineHeight;
103
100
  margin: 5px 0;
@@ -122,15 +119,12 @@ ol.steps {
122
119
 
123
120
  ol.steps > li:before,
124
121
  strong.step {
125
-
126
- background: @c1 + #666;
127
- color: @c1;
128
-
122
+ color: var(--cTxtAccent);
129
123
  text-align: center;
130
- width: @sizeStep * 1.2;
131
- height: @sizeStep * 1.2;
132
- line-height: @sizeStep * 1.2;
133
- border-radius: 50%;
124
+ font-weight: bold;
125
+
126
+ font-size: 1em;
127
+ line-height: 1.8em;
134
128
  }
135
129
 
136
130
  /*----------------------------------
@@ -32,7 +32,7 @@ i {
32
32
  }
33
33
 
34
34
  &.solid {
35
- color: var(--cAccent2);
35
+ color: var(--cTxtAccent2);
36
36
  background: var(--cBg);
37
37
  border-radius: @radius;
38
38
 
@@ -74,10 +74,10 @@ i.logo {
74
74
  line-height: 3.2em;
75
75
  flex: 0 0 3.2em;*/
76
76
 
77
- width: 2em;
78
- height: 2em;
79
- line-height: 2em;
80
- flex: 0 0 2em;
77
+ width: @sizeComponent * 0.9;
78
+ height: @sizeComponent * 0.9;
79
+ line-height: @sizeComponent * 0.9;
80
+ flex: 0 0 @sizeComponent * 0.9;
81
81
 
82
82
  border-radius: @radius;
83
83
  border: none; // For img``
@@ -122,7 +122,7 @@ em {
122
122
  display: block;
123
123
  height: 0.5em;
124
124
  border-radius: 0.25em;
125
- background: var(--cAccent2);
125
+ background: var(--cTxtAccent2);
126
126
 
127
127
  position: absolute;
128
128
  z-index: -1;
@@ -148,22 +148,9 @@ pre {
148
148
  @readingMargin: 2.4rem;
149
149
  .reading {
150
150
 
151
- --cTxtBase: #555;
152
-
153
151
  display: flex;
154
152
  flex-direction: column;
155
153
  gap: 1em;
156
-
157
- p,
158
- figcaption,
159
- h2,
160
- h3,
161
- > ul {
162
- max-width: var(--focusWidth);
163
- margin: 0 auto;
164
- width: 100%;
165
- //padding: 0 @readingMargin;
166
- }
167
154
 
168
155
  &,
169
156
  p {
@@ -175,7 +162,7 @@ pre {
175
162
 
176
163
  h2, h3, h4 {
177
164
  text-align: left;
178
- margin: 1em 0;
165
+ margin: 0.5em 0;
179
166
  }
180
167
 
181
168
  h2 {
@@ -215,6 +202,7 @@ pre {
215
202
  cursor: default;
216
203
  display: block;
217
204
  margin: 0 auto;
205
+ border-radius: @radius;
218
206
  }
219
207
 
220
208
  figcaption {
@@ -17,7 +17,7 @@ h3 {
17
17
  }
18
18
 
19
19
  strong {
20
- color: var(--cAccent);
20
+ color: var(--cTxtAccent);
21
21
  font-weight: inherit;
22
22
 
23
23
  .bg.img & {
@@ -50,10 +50,10 @@
50
50
 
51
51
  // Accent
52
52
  & when (@theme[accent1]) {
53
- --cAccent: @theme[accent1];
53
+ --cTxtAccent: @theme[accent1];
54
54
  }
55
55
  & when (@theme[accent2]) {
56
- --cAccent2: @theme[accent2];
56
+ --cTxtAccent2: @theme[accent2];
57
57
  }
58
58
 
59
59
  // Lines
@@ -143,7 +143,9 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
143
143
 
144
144
  if (!isToast)
145
145
  render = (
146
- <div class="modal">
146
+ <div class="modal" onClick={e =>
147
+ e.target.classList.contains('modal') && close(false)
148
+ }>
147
149
  {render}
148
150
  </div>
149
151
  )
@@ -36,7 +36,6 @@ export type Form<TFormData extends {} = {}> = {
36
36
  fields: FieldsAttrs<TFormData>,
37
37
  data: TFormData,
38
38
  options: TFormOptions<TFormData>,
39
- autosavedData?: Partial<TFormData>,
40
39
 
41
40
  // Actions
42
41
  validate: (data: Partial<TFormData>) => TValidationResult<{}>,
@@ -57,7 +56,7 @@ type FormState = {
57
56
  ----------------------------------*/
58
57
  export default function useForm<TFormData extends {}>(
59
58
  schema: Schema<TFormData>,
60
- options: TFormOptions<TFormData>
59
+ options: TFormOptions<TFormData> = {}
61
60
  ): [ Form, FieldsAttrs<TFormData> ] {
62
61
 
63
62
  const context = useContext();
@@ -66,31 +65,18 @@ export default function useForm<TFormData extends {}>(
66
65
  - INIT
67
66
  ----------------------------------*/
68
67
 
69
- // Autosaving data
70
- let autosavedData: TFormData | undefined;
71
- if (options.autoSave && typeof window !== 'undefined') {
72
- const autosaved = localStorage.getItem('form.' + options.autoSave.id);
73
- if (autosaved !== null) {
74
- try {
75
- console.log('[form] Parse autosaved from json:', autosaved);
76
- autosavedData = JSON.parse(autosaved);
77
- } catch (error) {
78
- console.error('[form] Failed to decode autosaved data from json:', autosaved);
79
- }
80
- }
81
- }
68
+ const [state, setState] = React.useState<FormState>({
69
+ hasChanged: options.data !== undefined,
70
+ isLoading: false,
71
+ errorsCount: 0,
72
+ errors: {}
73
+ });
82
74
 
83
75
  const initialData: Partial<TFormData> = options.data || {};
84
76
 
85
77
  // States
86
78
  const fields = React.useRef<FieldsAttrs<TFormData> | null>(null);
87
79
  const [data, setData] = React.useState< Partial<TFormData> >(initialData);
88
- const [state, setState] = React.useState<FormState>({
89
- hasChanged: false,
90
- isLoading: false,
91
- errorsCount: 0,
92
- errors: {}
93
- });
94
80
 
95
81
  // Validate data when it changes
96
82
  React.useEffect(() => {
@@ -99,8 +85,22 @@ export default function useForm<TFormData extends {}>(
99
85
  validate(data, false);
100
86
 
101
87
  // Autosave
102
- if (options.autoSave !== undefined)
103
- saveLocally(data, options.autoSave.id);
88
+ if (options.autoSave !== undefined) {
89
+
90
+ if (state.hasChanged)
91
+ saveLocally(data, options.autoSave.id);
92
+ else {
93
+ const autosaved = localStorage.getItem('form.' + options.autoSave.id);
94
+ if (autosaved !== null) {
95
+ try {
96
+ console.log('[form] Parse autosaved from json:', autosaved);
97
+ setData( JSON.parse(autosaved) );
98
+ } catch (error) {
99
+ console.error('[form] Failed to decode autosaved data from json:', autosaved);
100
+ }
101
+ }
102
+ }
103
+ }
104
104
 
105
105
  }, [data]);
106
106
 
@@ -235,7 +235,6 @@ export default function useForm<TFormData extends {}>(
235
235
  validate,
236
236
  submit,
237
237
  options,
238
- autosavedData,
239
238
  ...state
240
239
  }
241
240
 
@@ -382,7 +382,7 @@ export default function <TDonnee extends DonneesGraph, TTypeChartJs extends Char
382
382
 
383
383
  // Default color
384
384
  if (!dataset.colonne.color)
385
- dataset.colonne.color = css.getPropertyValue('--cAccent');
385
+ dataset.colonne.color = css.getPropertyValue('--cTxtAccent');
386
386
 
387
387
  return {
388
388
  label: dataset.colonne.label,
@@ -70,7 +70,7 @@
70
70
  }
71
71
 
72
72
  &.dragging {
73
- background: var(--cAccent);
73
+ background: var(--cTxtAccent);
74
74
  box-shadow: 0 0 8.3rem var(--cPrincipale);
75
75
  }
76
76
  }
@@ -14,7 +14,7 @@ div.progressbar {
14
14
  }
15
15
 
16
16
  > .progress {
17
- --cBg: var(--cAccent);
17
+ --cBg: var(--cTxtAccent);
18
18
  background: var(--cBg);
19
19
  width: 0%;
20
20
  max-width: 100%;
@@ -39,9 +39,9 @@ const hsl = (h: number, s: number = 80, l: number = 70) => `hsl(${Math.floor(h)}
39
39
  export const couleurViaPc = (pc: number, couleurs: [number, number], ecart: number) => {
40
40
 
41
41
  return {
42
- couleur1: 'var(--cAccent)',
43
- couleur2: 'var(--cAccent)',
44
- couleurTxt: 'var(--cAccent)'
42
+ couleur1: 'var(--cTxtAccent)',
43
+ couleur2: 'var(--cTxtAccent)',
44
+ couleurTxt: 'var(--cTxtAccent)'
45
45
  }
46
46
  const [couleurMin, couleurMax] = couleurs;
47
47
  const couleur = couleurMin + (pc * (couleurMax - couleurMin));
@@ -15,7 +15,7 @@
15
15
  height: @hTrack;
16
16
 
17
17
  &-0 {
18
- background: var(--cAccent);
18
+ background: var(--cTxtAccent);
19
19
  border-radius: @hTrack / 2 0 0 @hTrack / 2;
20
20
  }
21
21
 
@@ -39,6 +39,11 @@ export type Props = {
39
39
  validator?: ReturnType< SchemaValidators["number"] >,
40
40
  })
41
41
 
42
+ type TInputElementProps = Omit<(
43
+ JSX.HTMLAttributes<HTMLInputElement> &
44
+ JSX.HTMLAttributes<HTMLTextAreaElement>
45
+ ), 'onChange'>
46
+
42
47
  /*----------------------------------
43
48
  - COMPOSANT
44
49
  ----------------------------------*/
@@ -52,7 +57,7 @@ export default ({
52
57
  // Actions
53
58
  onPressEnter,
54
59
  ...props
55
- }: Props & InputBaseProps<string> & Omit<JSX.HTMLAttributes<HTMLInputElement>, 'onChange'>) => {
60
+ }: Props & InputBaseProps<string> & TInputElementProps) => {
56
61
 
57
62
  /*----------------------------------
58
63
  - INIT
@@ -21,14 +21,20 @@ export default ({ page }: { page: Page }) => {
21
21
  ----------------------------------*/
22
22
  const context = useContext();
23
23
 
24
- // Temporary fix: context.page may not be updated at this stage
25
- // Seems to be the case when we change page, but still same page component with different data
26
- context.page = page;
27
-
28
24
  // Bind data
29
25
  const [apiData, setApiData] = React.useState<{[k: string]: any} | null>( page.data || {});
30
26
  page.setAllData = setApiData;
31
- context.data = apiData;
27
+ const fullData = {
28
+ ...context.data,
29
+ ...apiData
30
+ }
31
+
32
+ // Temporary fix: context.page may not be updated at this stage
33
+ // Seems to be the case when we change page, but still same page component with different data
34
+ // TODO: ensure these updated are made every tume we change page / context
35
+ context.page = page;
36
+ context.data = fullData;
37
+ context.context = context;
32
38
 
33
39
  // Page component has not changed, but data were updated (ex: url parameters change)
34
40
  React.useEffect(() => {
@@ -43,15 +49,7 @@ export default ({ page }: { page: Page }) => {
43
49
  // Make request parameters and api data accessible from the page component
44
50
  return page.renderer ? (
45
51
 
46
- <page.renderer
47
- // Services
48
- {...context}
49
- // API data & URL params
50
- data={{
51
- ...apiData,
52
- ...context.request.data
53
- }}
54
- />
52
+ <page.renderer {...context} />
55
53
 
56
54
  ) : <>Renderer missing</>
57
55
  }
@@ -23,6 +23,7 @@ import { getLayout } from '@common/router/layouts';
23
23
  import { getRegisterPageArgs, buildRegex } from '@common/router/register';
24
24
  import { TFetcherList } from '@common/router/request/api';
25
25
  import type { TFrontRenderer } from '@common/router/response/page';
26
+ import Button from '../../components/button';
26
27
 
27
28
  import App from '@client/app/component';
28
29
  import type ClientApplication from '@client/app';
@@ -359,8 +360,20 @@ export default class ClientRouter<
359
360
 
360
361
  } catch (e) {
361
362
  console.error(`Failed to fetch the route ${route.chunk}`, e);
362
- this.app.handleError(new Error("Failed to load content. Please reload the page and try again."));
363
- throw e;
363
+ try {
364
+ this.context.modal.show(() => (
365
+ <div class="card col bg white w-3">
366
+ <h2>New Update Available!</h2>
367
+ <p>
368
+ A new version of the website is available. Please refresh the page to continue.
369
+ </p>
370
+ <Button type="primary" onClick={() => window.location.reload()}>
371
+ Reload
372
+ </Button>
373
+ </div>
374
+ ));
375
+ } catch (error) {}
376
+ throw new Error("A new version of the website is available. Please refresh the page.");
364
377
  }
365
378
 
366
379
  } else {
@@ -238,8 +238,9 @@ export default class ApiClient implements ApiClientService {
238
238
  const error = errorFromJson(errorData);
239
239
  throw error;
240
240
  }
241
- debug && console.log(`[api] Success:`, response);
242
- return response.json() as Promise<TData>;
241
+ const json = await response.json() as TData;
242
+ debug && console.log(`[api] Success:`, json);
243
+ return json;
243
244
  })
244
245
  .catch((error) => {
245
246
  if (error instanceof TypeError) {
@@ -56,6 +56,15 @@ md.block.ruler.after('list', 'test', (state, startLine, endLine, silent) => {
56
56
  token.attrs[ aIndex ][1] = 'liste'; // replace value of existing attr
57
57
  }
58
58
 
59
+ } else if (token.type === 'ordered_list_open') {
60
+
61
+ const aIndex = token.attrIndex('class');
62
+ if (aIndex < 0) {
63
+ token.attrPush(['class', 'steps']); // add new attribute
64
+ } else {
65
+ token.attrs[ aIndex ][1] = 'steps'; // replace value of existing attr
66
+ }
67
+
59
68
  }
60
69
  }
61
70
 
@@ -442,7 +442,7 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
442
442
  // Upsert
443
443
  let upsertStatement: string = '';
444
444
  if (opts.upsert !== undefined)
445
- upsertStatement = ' ' + this.buildUpsertStatement<TData>(table, opts as With<TInsertQueryOptions<TData>, 'upsert'>);
445
+ upsertStatement = ' ' + this.buildUpsertStatement<TData>(table, data, opts as With<TInsertQueryOptions<TData>, 'upsert'>);
446
446
 
447
447
  let okPacket: mysql.OkPacket = { ...emptyOkPacket }
448
448
 
@@ -501,10 +501,11 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
501
501
 
502
502
  private buildUpsertStatement<TData extends TObjetDonnees>(
503
503
  table: TMetasTable,
504
+ data: TData[],
504
505
  opts: With<TInsertQueryOptions<TData>, 'upsert'>
505
506
  ): string {
506
507
 
507
- const valuesToUpdate = this.getValuesToUpdate(table, opts.upsert);
508
+ const valuesToUpdate = this.getValuesToUpdate(table, data, opts.upsert);
508
509
 
509
510
  // All columns are ps
510
511
  const valuesToUpdatesEntries = Object.entries(valuesToUpdate);
@@ -519,35 +520,39 @@ export default class SQL extends Service<Config, Hooks, Application, Services> {
519
520
  // TODO: Fix typings
520
521
  private getValuesToUpdate<TData extends TObjetDonnees>(
521
522
  table: TMetasTable,
523
+ data: TData[],
522
524
  colsToUpdate: TColsToUpsert<TData>
523
525
  ) {
524
526
 
525
527
  // Column name => SQL
526
528
  let valuesToUpdate: Partial<TData> = {};
527
-
528
529
  // Define which columns to update when the record already exists
529
530
  let valuesNamesToUpdate: (keyof TData)[] = [];
530
- if (colsToUpdate === '*') {
531
+ let updateAll: boolean | undefined;
532
+
533
+ if (Array.isArray( colsToUpdate )) {
531
534
 
532
- valuesNamesToUpdate = Object.keys(table.colonnes);// table.columnNamesButPk;
533
- console.log(LogPrefix, `Automatic upsert into ${table.chemin} using ${table.pk.join(', ')} as pk: ${valuesNamesToUpdate.join(', ')}`);
534
- // We don't take columnNamesButPk, because if all the columns are pks, we don't have yny value for the ON DUPLICATE KEY
535
- // Meaning
535
+ valuesNamesToUpdate = colsToUpdate;
536
536
 
537
- } else if (Array.isArray( colsToUpdate )) {
537
+ } else if (colsToUpdate === '*') {
538
538
 
539
- valuesNamesToUpdate = colsToUpdate;
539
+ updateAll = true;
540
540
 
541
541
  } else {
542
542
 
543
- const { '*': updateAll, ...customValuesToUpdate } = colsToUpdate;
543
+ let customValuesToUpdate: Partial<TData>;
544
+ ({ '*': updateAll, ...customValuesToUpdate } = colsToUpdate);
544
545
 
545
546
  for (const colKey in customValuesToUpdate)
546
547
  valuesToUpdate[ colKey ] = this.esc(customValuesToUpdate[ colKey ], true);
547
548
 
548
- if (updateAll)
549
- valuesNamesToUpdate = Object.keys(table.colonnes);//table.columnNamesButPk;
549
+ }
550
550
 
551
+ if (updateAll) {
552
+ for (const record of data)
553
+ for (const key in record)
554
+ if (!valuesNamesToUpdate.includes( key ))
555
+ valuesNamesToUpdate.push( key );
551
556
  }
552
557
 
553
558
  for (const colToUpdate of valuesNamesToUpdate)
@@ -59,6 +59,11 @@ export default abstract class FsDriver<
59
59
  > extends Service<Config, {}, Application, Services> {
60
60
 
61
61
  public abstract mount(): Promise<void>;
62
+
63
+ public abstract getFileUrl(
64
+ bucketName: TBucketName,
65
+ filename: string
66
+ ): string;
62
67
 
63
68
  public abstract readDir( bucketName: TBucketName, dirname?: string ): Promise<SourceFile[]>;
64
69
 
@@ -72,6 +72,13 @@ export default class LocalFS<
72
72
  - ACTIONS
73
73
  ----------------------------------*/
74
74
 
75
+ public getFileUrl(
76
+ bucketName: TBucketName,
77
+ filename: string
78
+ ) {
79
+ throw new Error("Method not available for local files.");
80
+ }
81
+
75
82
  public async readDir( bucketName: TBucketName, dirname?: string ) {
76
83
 
77
84
  const bucketDir = this.config.buckets[bucketName];
@@ -99,6 +99,16 @@ export default class S3Driver<
99
99
  - ACTIONS
100
100
  ----------------------------------*/
101
101
 
102
+ public getFileUrl(
103
+ bucketName: TBucketName,
104
+ filename: string
105
+ ) {
106
+ const bucket = this.config.buckets[bucketName];
107
+ if (bucket === undefined)
108
+ throw new Error(`Bucket "${bucketName}" not found in configuration`);
109
+ return `https://${bucket}.s3.${this.config.region}.amazonaws.com/${filename}`
110
+ }
111
+
102
112
  public readDir( bucketName: TBucketName, dirname?: string ) {
103
113
  const bucket = this.config.buckets[bucketName];
104
114
  return new Promise<SourceFile[]>((resolve, reject) => {