5htp-core 0.2.2 → 0.2.3

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.
Files changed (43) hide show
  1. package/package.json +1 -1
  2. package/src/client/app/component.tsx +1 -2
  3. package/src/client/app/index.ts +1 -1
  4. package/src/client/assets/css/components/button.less +4 -3
  5. package/src/client/assets/css/components/card.less +26 -11
  6. package/src/client/assets/css/components/lists.less +1 -1
  7. package/src/client/assets/css/components/other.less +0 -11
  8. package/src/client/assets/css/components.less +17 -3
  9. package/src/client/assets/css/core.less +55 -0
  10. package/src/client/assets/css/theme.less +0 -4
  11. package/src/client/assets/css/utils/medias.less +0 -47
  12. package/src/client/components/Dialog/Manager.tsx +1 -4
  13. package/src/client/components/Form.ts +123 -0
  14. package/src/client/components/{Form → Form_old}/index.tsx +0 -0
  15. package/src/client/components/{Form → Form_old}/index.tsx.old +0 -0
  16. package/src/client/components/containers/Popover/index.tsx +38 -120
  17. package/src/client/components/data/progressbar/circular/index.tsx +1 -3
  18. package/src/client/components/inputv3/base.less +0 -1
  19. package/src/client/components/inputv3/base.tsx +5 -1
  20. package/src/client/components/inputv3/string/index.tsx +22 -9
  21. package/src/client/services/router/components/Page.tsx +16 -18
  22. package/src/client/services/router/components/router.tsx +3 -3
  23. package/src/client/services/router/index.tsx +17 -14
  24. package/src/client/services/router/request/api.ts +3 -2
  25. package/src/client/services/router/response/index.tsx +4 -0
  26. package/src/client/services/router/response/page.ts +2 -1
  27. package/src/common/router/index.ts +1 -1
  28. package/src/common/router/layouts.ts +38 -6
  29. package/src/common/router/register.ts +3 -9
  30. package/src/common/router/request/api.ts +3 -1
  31. package/src/common/validation/index.ts +1 -1
  32. package/src/common/validation/schema.ts +6 -1
  33. package/src/common/validation/validators.ts +1 -1
  34. package/src/server/app/index.ts +2 -1
  35. package/src/server/services/database/index.ts +9 -13
  36. package/src/server/services/router/index.ts +3 -3
  37. package/src/server/services/router/request/api.ts +3 -0
  38. package/src/server/services/router/response/index.ts +7 -4
  39. package/src/server/services/router/response/mask/Filter.ts +16 -72
  40. package/src/server/services/router/response/mask/index.ts +4 -7
  41. package/src/server/services/router/response/page/document.tsx +2 -2
  42. package/src/server/services/router/response/page/index.tsx +2 -2
  43. package/src/server/services/schema/request.ts +4 -2
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.2.2",
4
+ "version": "0.2.3",
5
5
  "author": "Gaetan Le Gac (https://github.com/gaetanlegac)",
6
6
  "repository": "git://github.com/gaetanlegac/5htp-core.git",
7
7
  "license": "MIT",
@@ -21,8 +21,7 @@ export default function App ({ context }: {
21
21
  context: TClientOrServerContext,
22
22
  }) {
23
23
 
24
- const route = context.route;
25
- const curLayout = route.options.layout;
24
+ const curLayout = context.page?.layout;
26
25
  const [layout, setLayout] = React.useState<Layout | false | undefined>(curLayout);
27
26
 
28
27
  // TODO: context.page is always provided in the context on the client side
@@ -116,7 +116,7 @@ export default abstract class Application {
116
116
  },
117
117
  body: JSON.stringify({
118
118
  url: window.location.pathname,
119
- ssrData: JSON.stringify(window["ssr"]),
119
+ context: JSON.stringify(window["ssr"]),
120
120
  guiVersion: BUILD_DATE,
121
121
  ...infos
122
122
  })
@@ -94,9 +94,6 @@
94
94
  ----------------------------------*/
95
95
 
96
96
  &.bg {
97
-
98
- background: var(--cBg);
99
- color: var(--cTxtBase);
100
97
 
101
98
  &:not(:disabled) {
102
99
  &:hover,
@@ -316,6 +313,10 @@ ul.col {
316
313
  }
317
314
  }
318
315
 
316
+ &.menu > li > .btn {
317
+ width: 100%;
318
+ }
319
+
319
320
  .btn.icon > .label,
320
321
  > li > .btn + ul {
321
322
  left: 100%;
@@ -20,23 +20,38 @@
20
20
 
21
21
  &.row {
22
22
  padding: @cardPadding @cardPaddingLong;
23
+
24
+ // TODO: rename & move elsewere
25
+ &.inside {
26
+
27
+ padding: 0;
28
+ gap: 0;
29
+ align-items: stretch;
30
+
31
+ > * {
32
+ padding: @cardPadding @cardPaddingLong;
33
+ &:first-child {
34
+ border-top-left-radius: inherit;
35
+ border-bottom-left-radius: inherit;
36
+ }
37
+ &:last-child {
38
+ border-top-right-radius: inherit;
39
+ border-bottom-right-radius: inherit;
40
+ }
41
+ }
42
+ }
43
+
44
+ &.sep {
45
+ > * + * {
46
+ border-left: solid 1px #eee;
47
+ }
48
+ }
23
49
  }
24
50
 
25
51
  &.col {
26
52
  padding: @cardPaddingLong @cardPadding;
27
53
  }
28
54
 
29
- &.bg {
30
- --cBg: difference(@cBgPage, #0a0a0a);
31
- --cBg2: difference(@cBgPage, #111);
32
- background: var(--cBg);
33
-
34
- &.img {
35
- background-position: center;
36
- background-size: cover;
37
- }
38
- }
39
-
40
55
  &.selected {
41
56
 
42
57
  box-shadow: 0 0 0 3px @c1;
@@ -123,7 +123,7 @@ ol.steps {
123
123
  ol.steps > li:before,
124
124
  strong.step {
125
125
 
126
- background: @c1SuperLight;
126
+ background: @c1 + #666;
127
127
  color: @c1;
128
128
 
129
129
  text-align: center;
@@ -27,17 +27,6 @@
27
27
  background: var(--cBgAccent);
28
28
  }
29
29
 
30
- .bg-cover {
31
- background-color: #000;
32
- background-repeat: no-repeat;
33
- background-position: center;
34
- background-size: cover;
35
- }
36
-
37
- img.bg-cover {
38
- object-fit: cover;
39
- }
40
-
41
30
  .clickable {
42
31
  cursor: pointer;
43
32
  }
@@ -31,7 +31,6 @@
31
31
  }
32
32
 
33
33
  .card,
34
- .input.text,
35
34
  .btn,
36
35
  .table,
37
36
  i.solid {
@@ -48,9 +47,24 @@ i.solid {
48
47
  }
49
48
  }
50
49
 
50
+ .input.text {
51
+ border: solid 0.25em #f5f5f5;
52
+
53
+ &:hover {
54
+ border-color: #eee;
55
+ }
56
+
57
+ &.focus {
58
+ border-color: @c1;
59
+ }
60
+
61
+ &.error {
62
+ border-color: @cError;
63
+ }
64
+ }
65
+
51
66
  //.card.clickable:hover,
52
67
  .btn:hover,
53
68
  .input.text.focus {
54
- z-index: 5;
55
- box-shadow: 0 10px 50px fade(#000, 15%);
69
+
56
70
  }
@@ -12,6 +12,61 @@
12
12
  .bg {
13
13
  background: var(--cBg);
14
14
  color: var(--cTxtBase);
15
+
16
+ &.img {
17
+ background-size: cover;
18
+ background-repeat: no-repeat;
19
+ background-position: center;
20
+
21
+ .build-theme-bg(#000, #fff);
22
+ text-shadow: 0 0 10px fade(#000, 20%);
23
+
24
+ --cTxtDiscret: fade(#fff, 40%)
25
+ --cTxtDesc: fade(#fff, 60%);
26
+ --cTxtBase: fade(#fff, 80%);
27
+ --cTxtImportant: fade(#fff, 100%);
28
+ --cTxtAccent: fade(#fff, 20%);
29
+
30
+ &.light {
31
+ --cTxtDiscret: fade(#000, 40%)
32
+ --cTxtDesc: fade(#000, 60%);
33
+ --cTxtBase: fade(#000, 80%);
34
+ --cTxtImportant: fade(#000, 100%);
35
+ --cTxtAccent: fade(#000, 20%);
36
+ }
37
+
38
+ &.colorize {
39
+ position: relative;
40
+
41
+ &::after {
42
+ content: ' ';
43
+ display: block;
44
+ background-color: inherit;
45
+ opacity: 0.5;
46
+ border-radius: inherit;
47
+
48
+ position: absolute;
49
+ top: 0; right: 0; bottom: 0; left: 0;
50
+ z-index: 1;
51
+ }
52
+
53
+ > * {
54
+ z-index: 2;
55
+ position: relative;
56
+ }
57
+ }
58
+ }
59
+
60
+ &.bg-cover {
61
+ background-color: #000;
62
+ background-repeat: no-repeat;
63
+ background-position: center;
64
+ background-size: cover;
65
+ }
66
+
67
+ &img.bg-cover {
68
+ object-fit: cover;
69
+ }
15
70
  }
16
71
 
17
72
  html {
@@ -45,10 +45,6 @@
45
45
  border: if(@bg = #ffffff, solid 2px var(--cLine), none);
46
46
  }*/
47
47
  }
48
-
49
- .overlay {
50
- background: fade(@bg, 50%);
51
- }
52
48
  }
53
49
 
54
50
  .build-theme-fg( @cTxtBase, @bg: @cBgPage, @apply: true ) {
@@ -4,25 +4,6 @@ img.img,
4
4
  background: @cBgPage - #101010;
5
5
  }
6
6
 
7
- .bg.img {
8
- .build-theme-bg(#000, #fff);
9
- text-shadow: 0 0 10px fade(#000, 20%);
10
-
11
- --cTxtDiscret: fade(#fff, 40%)
12
- --cTxtDesc: fade(#fff, 60%);
13
- --cTxtBase: fade(#fff, 80%);
14
- --cTxtImportant: fade(#fff, 100%);
15
- --cTxtAccent: fade(#fff, 20%);
16
-
17
- &.light {
18
- --cTxtDiscret: fade(#000, 40%)
19
- --cTxtDesc: fade(#000, 60%);
20
- --cTxtBase: fade(#000, 80%);
21
- --cTxtImportant: fade(#000, 100%);
22
- --cTxtAccent: fade(#000, 20%);
23
- }
24
- }
25
-
26
7
  img {
27
8
  max-width: 100%;
28
9
  display: block;
@@ -36,34 +17,6 @@ img {
36
17
  - IMAGE CONFIG
37
18
  ----------------------------------*/
38
19
 
39
- // Default: cover
40
- .bg.img {
41
- background-size: cover;
42
- background-repeat: no-repeat;
43
- background-position: center;
44
-
45
- &.colorize {
46
- position: relative;
47
-
48
- &::after {
49
- content: ' ';
50
- display: block;
51
- background-color: inherit;
52
- opacity: 0.5;
53
- border-radius: inherit;
54
-
55
- position: absolute;
56
- top: 0; right: 0; bottom: 0; left: 0;
57
- z-index: 1;
58
- }
59
-
60
- > * {
61
- z-index: 2;
62
- position: relative;
63
- }
64
- }
65
- }
66
-
67
20
  img.img {
68
21
  object-fit: cover;
69
22
  object-position: center;
@@ -137,10 +137,7 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
137
137
 
138
138
  if (!isToast)
139
139
  render = (
140
- <div class="modal" onClick={(e) => {
141
- if (e.target === e.currentTarget && !paramsInit?.prison)
142
- close(false);
143
- }}>
140
+ <div class="modal">
144
141
  {render}
145
142
  </div>
146
143
  )
@@ -0,0 +1,123 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // npm
6
+ import React from 'react';
7
+
8
+ // Core
9
+ import type { Schema } from '@common/validation';
10
+
11
+ /*----------------------------------
12
+ - TYPES
13
+ ----------------------------------*/
14
+ type TFormOptions<TFormData extends {}> = {
15
+ data?: Partial<TFormData>,
16
+ submit?: (data: TFormData) => Promise<void>
17
+ }
18
+
19
+ type FieldsAttrs<TFormData extends {}> = {
20
+ [fieldName in keyof TFormData]: {}
21
+ }
22
+
23
+ export type Form<TFormData extends {} = {}> = {
24
+ data: TFormData,
25
+ set: (data: Partial<TFormData>) => void,
26
+ submit: (additionnalData?: Partial<TFormData>) => Promise<any>,
27
+ fields: FieldsAttrs<TFormData>,
28
+ }
29
+
30
+ /*----------------------------------
31
+ - HOOK
32
+ ----------------------------------*/
33
+ export default function useForm<TFormData extends {}>( schema: Schema<TFormData>, options: TFormOptions<TFormData> ) {
34
+
35
+ /*----------------------------------
36
+ - INIT
37
+ ----------------------------------*/
38
+ const fields = React.useRef<FieldsAttrs<TFormData>>(null);
39
+
40
+ const [data, setData] = React.useState<TFormData>( options.data || {} );
41
+ const [state, setState] = React.useState({
42
+ isLoading: false,
43
+ errorsCount: 0,
44
+ errors: {},
45
+ validated: false
46
+ });
47
+
48
+ React.useEffect(() => {
49
+ state.validated && validate(data);
50
+ }, [data]);
51
+
52
+ /*----------------------------------
53
+ - ACTIONS
54
+ ----------------------------------*/
55
+ const validate = (allData: TFormData) => {
56
+
57
+ const validated = schema.validate(allData, allData);
58
+
59
+ // Update errors
60
+ if (validated.nbErreurs !== state.errorsCount) {
61
+ fields.current = null; // Update the fields definition
62
+ setState( old => ({
63
+ ...old,
64
+ errorsCount: validated.nbErreurs,
65
+ errors: validated.erreurs,
66
+ validated: true
67
+ }));
68
+ }
69
+
70
+ return validated;
71
+ }
72
+
73
+ const submit = (additionnalData: Partial<TFormData> = {}) => {
74
+
75
+ const allData = { ...data, ...additionnalData }
76
+
77
+ // Validation
78
+ const validated = validate(allData);
79
+ if (validated.nbErreurs !== 0)
80
+ return;
81
+
82
+ // Callback
83
+ if (options.submit)
84
+ return options.submit(validated.values);
85
+ }
86
+
87
+ if (fields.current === null){
88
+ fields.current = {}
89
+ for (const fieldName in schema.fields) {
90
+ fields.current[fieldName] = {
91
+
92
+ // Value control
93
+ value: data[fieldName],
94
+ onChange: (val) => setData( old => ({ ...old, [fieldName]: val })),
95
+
96
+ // Submit on press enter
97
+ onKeyDown: e => {
98
+ if (e.key === 'Enter') {
99
+ submit({ [fieldName]: e.target.value } as Partial<TFormData>);
100
+ }
101
+ },
102
+
103
+ // Error
104
+ errors: state.errors[ fieldName ],
105
+ required: schema.fields[ fieldName ].options?.opt !== true
106
+ }
107
+ }
108
+ }
109
+
110
+ /*----------------------------------
111
+ - EXPOSE
112
+ ----------------------------------*/
113
+
114
+ const form = {
115
+ data,
116
+ set: setData,
117
+ submit,
118
+ fields: fields.current,
119
+ ...state
120
+ }
121
+
122
+ return [form, fields.current]
123
+ }
@@ -5,6 +5,7 @@
5
5
  // Npm
6
6
  import React, { JSX } from 'react';
7
7
  import { ComponentChild } from 'preact';
8
+ import type { StateUpdater } from 'preact/hooks';
8
9
 
9
10
  // Composants
10
11
  import Bouton, { Props as PropsBouton } from '@client/components/button';
@@ -24,34 +25,17 @@ import useContexte from '@/client/context';
24
25
  export type Props = {
25
26
  id?: string,
26
27
 
27
- children: JSX.Element | [JSX.Element],
28
- afficher?: boolean,
29
- fermer?: (funcFermer: () => void) => void,
30
- position?: TSide,
31
- frame?: HTMLElement,
32
-
28
+ // Display
33
29
  content?: JSX.Element,
34
- menu?: { actions: TAction<any>[], data: any },
35
-
36
- tag?: string,
30
+ state: [boolean, StateUpdater<boolean>],
37
31
  width?: number | string,
38
- desactiver?: boolean
39
- }
40
-
41
- export type TAction<TDonnee> = {
42
- icone?: TIcons,
43
- label: ComponentChild,
44
- multi?: boolean,
45
-
46
- onClick?: (donnees: TDonnee, index: number) => void,
47
- lien?: (donnees: TDonnee, index: number) => string,
48
- bouton?: (donnees: TDonnee, index: number) => PropsBouton
49
- }
50
-
51
- export type TActionsPopover = {
52
- show: () => void,
53
- hide: () => void,
54
- toggle: () => void
32
+ disable?: boolean
33
+ // Position
34
+ frame?: HTMLElement,
35
+ side?: TSide,
36
+ // Tag
37
+ children: JSX.Element | [JSX.Element],
38
+ tag?: string,
55
39
  }
56
40
 
57
41
  /*----------------------------------
@@ -64,116 +48,58 @@ export default (props: Props) => {
64
48
 
65
49
  let {
66
50
  id,
67
- children, content, menu, tag, width, onVisibleChange, interactions, desactiver, fermer, frame,
68
- position, afficher,
51
+
52
+ content, state, width, disable,
53
+
54
+ frame, side,
55
+
56
+ children, tag,
57
+
69
58
  ...autresProps
70
59
  } = props;
71
60
 
72
- const [state, setState] = React.useState({
73
- aff: false,
74
- position: undefined
75
- });
76
-
61
+ const [position, setPosition] = React.useState(undefined);
77
62
  const refCont = React.useRef<HTMLElement>(null);
78
63
  const refPop = React.useRef<HTMLElement>(null);
79
64
 
80
- const controleViaProps = 'afficher' in props;
81
- if (!controleViaProps) {
82
- afficher = state.aff;
83
- fermer = () => setState((stateA) => ({ ...stateA, aff: false }));
84
- }
85
-
86
- // L'assignement d'un id à une popover permet de controler l'affichage de ce dernier depuis n'importe quel autre composant
87
- if (id !== undefined) {
88
- ctx.popovers[ id ] = {
89
- show: () => setState(s => ({ ...s, aff: true })),
90
- hide: () => setState(s => ({ ...s, aff: false })),
91
- toggle: () => setState(s => ({ ...s, aff: !s.aff })),
92
- }
93
- }
65
+ const [shown, show] = state;
94
66
 
95
67
  // Màj visibilite
96
68
  React.useEffect(() => {
97
- if (afficher === true) {
98
-
69
+ if (shown === true) {
99
70
  // Positionnement si affichage
100
- setState((stateA) => ({
101
- ...stateA,
102
- position: getPosition(
71
+ setPosition(
72
+ getPosition(
103
73
  refCont.current,
104
74
  refPop.current,
105
75
  false,
106
76
  position,
107
77
  frame || document.getElementById('page')
108
78
  )
109
- }));
110
-
111
- const isPageElement = deepContains([document.getElementById('page')], refPop.current);
112
- if (!isPageElement)
113
- document.body.classList.add('focus-popup');
114
-
115
- } else {
116
-
117
- document.body.classList.remove('focus-popup');
79
+ );
118
80
 
81
+ //return blurable([refCont, () => show(false)])
119
82
  }
120
83
 
121
- if (onVisibleChange)
122
- onVisibleChange(afficher);
123
-
124
- if (afficher === true)
125
- return blurable([refCont, () => fermer()])
126
-
127
- }, [afficher]);
84
+ }, [shown]);
128
85
 
129
86
  if (!autresProps.className)
130
87
  autresProps.className = '';
131
88
 
132
89
  autresProps.className += ' contPopover';
133
90
 
134
- const active = afficher && !desactiver;
91
+ const active = shown && !disable;
135
92
  if (active) {
136
93
  autresProps.className += ' active';
137
94
  }
138
95
 
139
- /*if (props.nom === 'destType')
140
- console.log('AFFICHER POPOVER', afficher, 'controleViaProps', controleViaProps, 'autresProps.className', autresProps.className);*/
141
-
142
96
  const Tag = tag || 'div';
143
97
 
144
- if (!Array.isArray( children ))
145
- children = [children];
146
-
147
- if (menu !== undefined)
148
- content = (
149
- <ul className="menu v">
150
- {menu.actions.map(({ multi, bouton, lien, label, icone, onClick }: TAction<TDonnee>) => (
151
- <li>
152
- <Bouton
153
- {...(bouton ? (multi
154
- ? bouton([menu.data], [menu.index])
155
- : bouton(menu.data, menu.index)
156
- ) : {})}
157
- icon={icone}
158
- onClick={onClick && (() => multi
159
- ? onClick([menu.data], [menu.index])
160
- : onClick(menu.data, menu.index)
161
- )}
162
- link={lien && lien(menu.data, menu.index)}
163
- forme="lien"
164
- >
165
- {label}
166
- </Bouton>
167
- </li>
168
- ))}
169
- </ul>
170
- )
171
-
172
98
  return (
173
99
  <Tag
174
100
  style={{
175
101
  position: 'relative',
176
- ...(afficher ? {
102
+ ...(shown ? {
177
103
  zIndex: 11
178
104
  } : {})
179
105
  }}
@@ -183,33 +109,25 @@ export default (props: Props) => {
183
109
  }}
184
110
  {...autresProps}
185
111
  >
186
- {controleViaProps
187
- ? children // Les events de clic sont controlés par les props afficher et fermer
188
- : children.map(child => React.cloneElement(child, {
189
- onClick: () => {
190
- const nouvEtat = !afficher;
191
-
192
- setState({ aff: nouvEtat, position: state.position });
193
- },
194
- }))}
112
+ {React.cloneElement( children, {
113
+ onClick: (e) => {
114
+ show(isShown => !isShown);
115
+ }
116
+ })}
195
117
 
196
118
  {active && React.cloneElement(content, {
197
- className: (content.props.className || '') + ' card white popover' + (state.position ? ' pos_' + state.position.cote : ''),
198
- onClick: () => {
199
- if (fermer && !interactions)
200
- fermer();
201
- },
119
+ className: (content.props.className || '') + ' card white popover' + (position ? ' pos_' + position.cote : ''),
202
120
  ref: (ref: any) => {
203
121
  if (ref !== null)
204
122
  refPop.current = ref
205
123
  },
206
124
  style: {
207
125
  ...(content.props.style || {}),
208
- ...(state.position ? {
209
- top: state.position.css.top,
210
- left: state.position.css.left,
211
- right: state.position.css.right,
212
- bottom: state.position.css.bottom,
126
+ ...(position ? {
127
+ top: position.css.top,
128
+ left: position.css.left,
129
+ right: position.css.right,
130
+ bottom: position.css.bottom,
213
131
  } : {}),
214
132
  ...(width !== undefined ? { width: typeof width === 'number' ? width + 'rem' : width } : {})
215
133
  }
@@ -19,8 +19,6 @@ import {
19
19
  - TYPES
20
20
  ----------------------------------*/
21
21
 
22
- //import { TTailleComposant } from '@client/components/_base/types';
23
-
24
22
  type TEtape = {
25
23
  val: number,
26
24
  label?: string
@@ -44,7 +42,7 @@ export default ({
44
42
  ratioCercle: number,
45
43
  epaisseur: number,
46
44
  etapes?: TEtape[],
47
- taille?: TTailleComposant
45
+ taille?: TComponentSize
48
46
  }) => {
49
47
 
50
48
  const refCont = React.useRef<HTMLDivElement>(null);