5htp-core 0.4.6 → 0.4.7-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.
Files changed (41) hide show
  1. package/package.json +6 -1
  2. package/src/client/assets/css/components/table.less +70 -7
  3. package/src/client/components/Select/ChoiceSelector.tsx +6 -5
  4. package/src/client/components/Select/index.tsx +138 -131
  5. package/src/client/components/Table/index.tsx +67 -15
  6. package/src/client/components/index.ts +2 -1
  7. package/src/client/components/inputv3/Rte/ExampleTheme.tsx +42 -0
  8. package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +167 -0
  9. package/src/client/components/inputv3/Rte/icons/LICENSE.md +5 -0
  10. package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +4 -0
  11. package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +4 -0
  12. package/src/client/components/inputv3/Rte/icons/journal-text.svg +5 -0
  13. package/src/client/components/inputv3/Rte/icons/justify.svg +3 -0
  14. package/src/client/components/inputv3/Rte/icons/text-center.svg +3 -0
  15. package/src/client/components/inputv3/Rte/icons/text-left.svg +3 -0
  16. package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +3 -0
  17. package/src/client/components/inputv3/Rte/icons/text-right.svg +3 -0
  18. package/src/client/components/inputv3/Rte/icons/type-bold.svg +3 -0
  19. package/src/client/components/inputv3/Rte/icons/type-italic.svg +3 -0
  20. package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +3 -0
  21. package/src/client/components/inputv3/Rte/icons/type-underline.svg +3 -0
  22. package/src/client/components/inputv3/Rte/index.tsx +163 -0
  23. package/src/client/components/inputv3/Rte/style.less +428 -0
  24. package/src/client/components/inputv3/base.less +20 -33
  25. package/src/client/components/inputv3/base.tsx +36 -2
  26. package/src/client/components/inputv3/index.tsx +45 -44
  27. package/src/common/data/rte/index.ts +66 -0
  28. package/src/common/data/rte/nodes.ts +20 -0
  29. package/src/common/router/request/index.ts +4 -1
  30. package/src/common/validation/validators.ts +74 -1
  31. package/src/server/app/index.ts +0 -11
  32. package/src/server/services/auth/index.ts +86 -47
  33. package/src/server/services/auth/router/request.ts +9 -5
  34. package/src/server/services/database/index.ts +1 -1
  35. package/src/server/services/database/metas.ts +1 -1
  36. package/src/server/services/router/index.ts +2 -1
  37. package/src/server/services/router/response/index.ts +5 -2
  38. package/src/client/components/input/Rte/index.less +0 -13
  39. package/src/client/components/input/Rte/index.tsx +0 -143
  40. package/src/client/components/input/Rte/selection.ts +0 -34
  41. package/src/common/data/rte.tsx +0 -11
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.6",
4
+ "version": "0.4.7-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",
@@ -13,6 +13,9 @@
13
13
  "framework"
14
14
  ],
15
15
  "dependencies": {
16
+ "@lexical/headless": "^0.18.0",
17
+ "@lexical/html": "^0.18.0",
18
+ "@lexical/react": "^0.18.0",
16
19
  "accepts": "^1.3.7",
17
20
  "activity-detector": "^3.0.0",
18
21
  "ansi-to-html": "^0.7.1",
@@ -44,7 +47,9 @@
44
47
  "intl": "^1.2.5",
45
48
  "iso-639-1": "^2.1.9",
46
49
  "js-cookie": "^3.0.1",
50
+ "jsdom": "^25.0.1",
47
51
  "jsonwebtoken": "^8.5.1",
52
+ "lexical": "^0.18.0",
48
53
  "load-script": "^2.0.0",
49
54
  "locale": "^0.1.0",
50
55
  "markdown-it": "^13.0.1",
@@ -1,5 +1,6 @@
1
1
  .table {
2
2
  overflow: auto;
3
+ max-height: 90vh;
3
4
 
4
5
  > table {
5
6
  border-collapse: collapse;
@@ -13,7 +14,7 @@
13
14
  }
14
15
 
15
16
  &.card {
16
- padding: 1em 0;
17
+ padding: 0;
17
18
  }
18
19
  }
19
20
 
@@ -26,7 +27,7 @@ table {
26
27
 
27
28
  // By default, chrome disables text inherits
28
29
  line-height: inherit;
29
- font-size: inherit;
30
+ font-size: 0.9em;
30
31
 
31
32
  th {
32
33
  font-weight: 500;
@@ -38,8 +39,8 @@ table {
38
39
  padding: 1em 1em;
39
40
  text-align: left;
40
41
 
41
- + td, th {
42
- border-left: solid 1px #F5F5F5
42
+ &:not(:last-child) {
43
+ border-right: solid 1px #F5F5F5;
43
44
  }
44
45
 
45
46
  &:first-child {
@@ -50,6 +51,29 @@ table {
50
51
  padding-right: 2em;
51
52
  text-align: right;
52
53
  }
54
+
55
+ &.stickyColumn {
56
+ position: sticky;
57
+ background: var(--cBg);
58
+ white-space: break-spaces;
59
+ z-index: 1;
60
+
61
+ &:first-child {
62
+ left: 0;
63
+ }
64
+
65
+ &:last-child {
66
+ right: 0;
67
+ }
68
+ }
69
+ }
70
+
71
+ thead {
72
+ position: sticky;
73
+ top: 0;
74
+ background: var(--cBg);
75
+ white-space: break-spaces;
76
+ z-index: 5;
53
77
  }
54
78
 
55
79
  tbody {
@@ -57,11 +81,45 @@ table {
57
81
  td, th {
58
82
  vertical-align: middle;
59
83
  border-top: solid 1px var(--cLine);
84
+ position: relative;
85
+ }
86
+
87
+ // Rows: Crop when too long
88
+ td {
89
+
90
+ cursor: default;
91
+
92
+ &.extendable > .row {
93
+
94
+ max-width: 20em;
95
+ overflow: hidden;
96
+ justify-content: flex-start;
97
+
98
+ &::after {
99
+ content: ' ';
100
+ display: block;
101
+ position: absolute;
102
+
103
+ top: 0;
104
+ right: 0;
105
+ width: 30%;
106
+ height: 100%;
107
+
108
+ background: linear-gradient(to right, rgba(255, 255, 255, 0), var(--cBg));
109
+ }
110
+
111
+ > * {
112
+ flex-shrink: 0;
113
+ }
114
+
115
+ > .badge {
116
+ font-size: 0.8em;
117
+ }
118
+ }
60
119
  }
61
120
 
62
- &.actuel td {
63
- background: var(--cPrincipale);
64
- color: #fff;
121
+ tr:hover > td {
122
+ background: #FAFAFA;
65
123
  }
66
124
  }
67
125
 
@@ -112,4 +170,9 @@ table {
112
170
  }
113
171
  }
114
172
  }*/
173
+ }
174
+
175
+ .modal .tableCellExtended .row {
176
+ flex-wrap: wrap;
177
+ justify-content: flex-start;
115
178
  }
@@ -18,9 +18,7 @@ import type { TDialogControls } from '@client/components/dropdown';
18
18
 
19
19
  export type Choice = { label: ComponentChild, value: string }
20
20
 
21
- export type Choices = Choice[]
22
-
23
- type ChoicesFunc = (search: string) => Promise<Choices>
21
+ type ChoicesFunc = (search: string) => Promise<Choice[]>
24
22
 
25
23
  export type Props = (
26
24
  {
@@ -37,10 +35,13 @@ export type Props = (
37
35
  validator?: StringValidator
38
36
  }
39
37
  ) & {
40
- choices: Choices | ChoicesFunc,
38
+ choices: Choice[] | ChoicesFunc | string[],
41
39
  enableSearch?: boolean,
42
40
  required?: boolean,
43
41
  noneSelection?: false | string,
42
+ }
43
+
44
+ type SelectorProps = Props & {
44
45
  currentList: Choice[],
45
46
  refDropdown?: RefObject<TDialogControls>
46
47
  }
@@ -67,7 +68,7 @@ export default React.forwardRef<HTMLDivElement, Props>(({
67
68
  currentList,
68
69
  refDropdown,
69
70
  ...otherProps
70
- }: Props, ref) => {
71
+ }: SelectorProps, ref) => {
71
72
 
72
73
 
73
74
 
@@ -8,6 +8,7 @@ import React from 'react';
8
8
  // Core
9
9
  import { Props as DropdownProps } from '@client/components/dropdown';
10
10
  import { Popover, Button, Input } from '@client/components';
11
+ import { InputWrapper, InputBaseProps } from '@client/components/inputv3/base';
11
12
 
12
13
  // Specific
13
14
  import {
@@ -21,10 +22,8 @@ import ChoiceElement from './ChoiceElement';
21
22
  - TYPES
22
23
  ----------------------------------*/
23
24
 
24
- export type Props = SelectorProps & {
25
- dropdown: boolean | DropdownProps,
26
- title: string,
27
- errors?: string[],
25
+ export type Props = SelectorProps & Omit<InputBaseProps<Choice>, 'value'> & {
26
+ dropdown?: boolean | DropdownProps,
28
27
  }
29
28
 
30
29
  export type { Choice } from './ChoiceSelector';
@@ -51,24 +50,27 @@ const ensureChoice = (choice: Choice | string, choices: Choice[]): Choice => {
51
50
  /*----------------------------------
52
51
  - COMONENT
53
52
  ----------------------------------*/
54
- export default ({
55
- // Input basics
56
- title,
57
- errors,
58
- icon,
59
- required,
60
- validator,
61
-
62
- // Choice selection
63
- choices: initChoices,
64
- noneSelection,
65
- enableSearch,
66
- value: current,
67
- onChange: onChangeCallback,
68
- multiple,
69
- dropdown,
70
- ...otherProps
71
- }: Props) => {
53
+ export default (props: Props) => {
54
+
55
+ let {
56
+ // Input basics
57
+ title,
58
+ errors,
59
+ icon,
60
+ required,
61
+ validator,
62
+ wrapper = true,
63
+
64
+ // Choice selection
65
+ choices: initChoices,
66
+ noneSelection,
67
+ enableSearch,
68
+ value: current,
69
+ onChange: onChangeCallback,
70
+ multiple,
71
+ dropdown,
72
+ ...otherProps
73
+ } = props;
72
74
 
73
75
  /*----------------------------------
74
76
  - INIT
@@ -77,8 +79,10 @@ export default ({
77
79
  const popoverState = React.useState(false);
78
80
 
79
81
  const choicesViaFunc = typeof initChoices === 'function';
80
- if (choicesViaFunc && enableSearch === undefined)
82
+ if (choicesViaFunc)
81
83
  enableSearch = true;
84
+ else if (typeof initChoices[0] === 'string')
85
+ initChoices = initChoices.map( c => ({ label: c, value: c }));
82
86
 
83
87
  const refInputSearch = React.useRef<HTMLInputElement | null>(null);
84
88
 
@@ -159,119 +163,122 @@ export default ({
159
163
  />
160
164
  )
161
165
 
162
- return dropdown ? (
163
- <Popover {...(dropdown === true ? {
164
- width: '200px'
165
- } : dropdown)} content={(
166
- <div class="card bg white col al-top">
167
-
168
- {Search}
169
-
170
- {selectedItems.length !== 0 && (
171
- <ul class="row al-left wrap sp-05">
172
- {selectedItems.map( choice => (
173
- <ChoiceElement format='badge' choice={choice}
174
- currentList={currentList}
175
- onChange={onChange}
176
- multiple={multiple}
177
- includeCurrent
178
- />
179
- ))}
180
- </ul>
181
- )}
182
-
183
- {search.loading ? (
184
- <div class="row al-center h-2">
185
- <i src="spin" />
166
+ return (
167
+ <InputWrapper {...props}>
168
+ {dropdown ? (
169
+ <Popover {...(dropdown === true ? {
170
+ width: '200px'
171
+ } : dropdown)} content={(
172
+ <div class="card bg white col al-top">
173
+
174
+ {Search}
175
+
176
+ {selectedItems.length !== 0 && (
177
+ <ul class="row al-left wrap sp-05">
178
+ {selectedItems.map( choice => (
179
+ <ChoiceElement format='badge' choice={choice}
180
+ currentList={currentList}
181
+ onChange={onChange}
182
+ multiple={multiple}
183
+ includeCurrent
184
+ />
185
+ ))}
186
+ </ul>
187
+ )}
188
+
189
+ {search.loading ? (
190
+ <div class="row al-center h-2">
191
+ <i src="spin" />
192
+ </div>
193
+ ) : (
194
+ <ul class="menu col">
195
+ {choices.map( choice => (
196
+ <ChoiceElement format='list' choice={choice}
197
+ currentList={currentList}
198
+ onChange={onChange}
199
+ multiple={multiple}
200
+ includeCurrent
201
+ />
202
+ ))}
203
+ </ul>
204
+ )}
186
205
  </div>
187
- ) : (
188
- <ul class="menu col">
189
- {choices.map( choice => (
190
- <ChoiceElement format='list' choice={choice}
191
- currentList={currentList}
192
- onChange={onChange}
193
- multiple={multiple}
194
- includeCurrent
195
- />
196
- ))}
197
- </ul>
198
- )}
199
- </div>
200
- )} state={popoverState}>
201
- <Button type="secondary" icon={icon} iconR="chevron-down" {...otherProps}>
202
-
203
- {currentList.length === 0 ? <>
204
- {title}
205
- </> : multiple ? <>
206
- {title} <span class="badge s bg accent">{currentList.length}</span>
207
- </> : <>
208
- {currentList[0].label}
209
- </>}
210
-
211
- {errors?.length && (
212
- <div class="bubble bg error bottom">
213
- {errors.join('. ')}
214
- </div>
215
- )}
216
-
217
- </Button>
218
- </Popover>
219
- ) : (
220
-
221
- <div class="col sp-05">
222
- <div class={className} onMouseDown={() => refInputSearch.current?.focus()}>
223
-
224
- <div class="row al-left wrap pd-1">
225
-
226
- {icon !== undefined && (
227
- <i src={icon} />
228
- )}
206
+ )} state={popoverState}>
207
+ <Button type="secondary" icon={icon} iconR="chevron-down" {...otherProps}>
208
+
209
+ {currentList.length === 0 ? <>
210
+ {title}
211
+ </> : multiple ? <>
212
+ {title} <span class="badge s bg accent">{currentList.length}</span>
213
+ </> : <>
214
+ {currentList[0].label}
215
+ </>}
216
+
217
+ {errors?.length && (
218
+ <div class="bubble bg error bottom">
219
+ {errors.join('. ')}
220
+ </div>
221
+ )}
222
+
223
+ </Button>
224
+ </Popover>
225
+ ) : (
226
+ <div class="col sp-05">
227
+ <div class={className} onMouseDown={() => refInputSearch.current?.focus()}>
228
+
229
+ <div class="row al-left wrap pd-1">
230
+
231
+ {icon !== undefined && (
232
+ <i src={icon} />
233
+ )}
234
+
235
+ <div class="col al-left sp-05">
236
+
237
+ <label>{title}{isRequired && (
238
+ <span class="fg error">&nbsp;*</span>
239
+ )}</label>
240
+
241
+ <div class="row al-left wrap sp-05">
242
+
243
+ {selectedItems.map( choice => (
244
+ <ChoiceElement format='badge' choice={choice}
245
+ currentList={currentList}
246
+ onChange={onChange}
247
+ multiple={multiple}
248
+ includeCurrent
249
+ />
250
+ ))}
251
+
252
+ {Search}
253
+ </div>
254
+ </div>
229
255
 
230
- <div class="col al-left sp-05">
231
-
232
- <label>{title}{isRequired && (
233
- <span class="fg error">&nbsp;*</span>
234
- )}</label>
235
-
236
- <div class="row al-left wrap sp-05">
237
-
238
- {selectedItems.map( choice => (
239
- <ChoiceElement format='badge' choice={choice}
240
- currentList={currentList}
241
- onChange={onChange}
242
- multiple={multiple}
243
- includeCurrent
244
- />
245
- ))}
246
-
247
- {Search}
248
256
  </div>
249
- </div>
250
-
251
- </div>
252
257
 
253
- <div class="pd-1">
254
- <ul class="row al-left wrap sp-05" style={{
255
- maxHeight: '30vh',
256
- overflowY: 'auto'
257
- }}>
258
- {choices.map( choice => (
259
- <ChoiceElement format='badge' choice={choice}
260
- currentList={currentList}
261
- onChange={onChange}
262
- multiple={multiple}
263
- includeCurrent
264
- />
265
- ))}
266
- </ul>
267
- </div>
268
-
269
- </div>
270
- {errors?.length && (
271
- <div class="bubble bg error bottom">
272
- {errors.join('. ')}
258
+ <div class="pd-1">
259
+ <ul class="row al-left wrap sp-05" style={{
260
+ maxHeight: '30vh',
261
+ overflowY: 'auto'
262
+ }}>
263
+ {choices.map( choice => (
264
+ <ChoiceElement format='badge' choice={choice}
265
+ currentList={currentList}
266
+ onChange={onChange}
267
+ multiple={multiple}
268
+ includeCurrent
269
+ />
270
+ ))}
271
+ </ul>
272
+ </div>
273
+
274
+ </div>
275
+ {errors?.length && (
276
+ <div class="bubble bg error bottom">
277
+ {errors.join('. ')}
278
+ </div>
279
+ )}
273
280
  </div>
274
281
  )}
275
- </div>
282
+ </InputWrapper>
276
283
  )
277
284
  }
@@ -2,11 +2,13 @@
2
2
  /*----------------------------------
3
3
  - DEPENDANCES
4
4
  ----------------------------------*/
5
+
5
6
  // Libs
6
7
  import React from 'react';
7
- import { ComponentChild } from 'preact';
8
+ import { JSX, ComponentChild } from 'preact';
8
9
 
9
10
  // Composants
11
+ import useContext from '@/client/context';
10
12
  import Button, { Props as TButtonProps } from '@client/components/button';
11
13
  import Popover from '../containers/Popover';
12
14
  import Checkbox from '../inputv3/Checkbox';
@@ -21,6 +23,7 @@ export type Props<TRow> = {
21
23
 
22
24
  data: TRow[],
23
25
  columns: (row: TRow, rows: TRow[], index: number) => TColumn[];
26
+ stickyHeader?: boolean,
24
27
 
25
28
  setData?: (rows: TRow[]) => void,
26
29
  empty?: ComponentChild | false,
@@ -29,11 +32,11 @@ export type Props<TRow> = {
29
32
  actions?: TAction<TRow>[]
30
33
  }
31
34
 
32
- export type TColumn = {
35
+ export type TColumn = JSX.HTMLAttributes<HTMLElement> & {
33
36
  label: ComponentChild,
34
37
  cell: ComponentChild,
35
38
  raw?: number | string | boolean,
36
- class?: string
39
+ stick?: boolean,
37
40
  }
38
41
 
39
42
  export type TAction<TRow> = Omit<TButtonProps, 'onClick'> & {
@@ -47,13 +50,16 @@ export type TAction<TRow> = Omit<TButtonProps, 'onClick'> & {
47
50
  - COMPOSANTS
48
51
  ----------------------------------*/
49
52
  export default function Liste<TRow extends TDonneeInconnue>({
53
+ stickyHeader,
50
54
  data: rows, setData, empty,
51
55
  columns, actions, ...props
52
56
  }: Props<TRow>) {
53
57
 
58
+ const { modal } = useContext();
59
+
54
60
  if (rows.length === 0)
55
61
  return empty === false ? null : (
56
- <div class="pd-2 col al-center">
62
+ <div class={"pd-2 col al-center " + (props.className || '')}>
57
63
  {empty || <>
58
64
  <i src="meh-rolling-eyes" class="xl" />
59
65
  Uh ... No rows here.
@@ -92,29 +98,75 @@ export default function Liste<TRow extends TDonneeInconnue>({
92
98
  </td>
93
99
  )}
94
100
 
95
- {columns(row, rows, iDonnee).map((col) => {
101
+ {columns(row, rows, iDonnee).map(({
102
+ label, cell, class: className, raw,
103
+ stick, width, ...cellProps
104
+ }) => {
105
+
106
+ let classe = className || '';
107
+ if (typeof raw === 'number')
108
+ classe += 'txtRight';
109
+
110
+ if (stick) {
111
+ classe += ' stickyColumn';
112
+ }
113
+
114
+ if (width) {
115
+
116
+ if (cellProps.style === undefined)
117
+ cellProps.style = {};
118
+
119
+ cellProps.style = {
120
+ ...cellProps.style,
121
+ minWidth: width,
122
+ width: width,
123
+ maxWidth: width,
124
+ }
125
+ }
96
126
 
97
127
  if (iDonnee === 0) renduColonnes.push(
98
- <th>
99
- {col.label}
128
+ <th class={classe} {...cellProps}>
129
+ {label}
100
130
  </th>
101
131
  );
102
132
 
103
- const affichageBrut = ['number', 'string'].includes(typeof col.cell) || React.isValidElement(col.cell);
133
+ let render: ComponentChild;
134
+ if (Array.isArray(cell)) {
104
135
 
105
- let classe = col.class || '';
106
- if (typeof col.raw === 'number')
107
- classe += 'txtRight';
136
+ classe += ' extendable';
137
+
138
+ render = (
139
+ <div class="row sp-05">
140
+ {cell.map((item, i) => (
141
+ <span class={"badge bg light" + ((i % 7) + 1)}>
142
+ {item}
143
+ </span>
144
+ ))}
145
+ </div>
146
+ )
147
+
148
+ // Extension
149
+ cellProps.onClick = () => modal.show(() => (
150
+ <div class="card col tableCellExtended">
151
+ <h3>{label}</h3>
152
+ {render}
153
+ </div>
154
+ ));
155
+
156
+ } else if (['number', 'string'].includes(typeof cell) || React.isValidElement(cell)) {
157
+ render = cell;
158
+ } else
159
+ render = JSON.stringify(cell);
108
160
 
109
161
  return (
110
- <td class={classe}>
111
- {affichageBrut ? col.cell : JSON.stringify(col.cell)}
162
+ <td class={classe} {...cellProps}>
163
+ {render}
112
164
  </td>
113
165
  )
114
166
  })}
115
167
 
116
168
  {actions !== undefined && (
117
- <td>
169
+ <td class="stickyColumn">
118
170
  <Popover content={(
119
171
  <ul class="col menu card bg white">
120
172
  {actions.map(({ label, onClick, ...props }: TAction<TRow>) => (
@@ -148,7 +200,7 @@ export default function Liste<TRow extends TDonneeInconnue>({
148
200
  return <>
149
201
  <div {...props}>
150
202
  <table>
151
- <thead>
203
+ <thead className={stickyHeader ? 'stickyHeader' : undefined}>
152
204
  <tr>
153
205
  {selectionMultiple && (
154
206
  <th>
@@ -27,4 +27,5 @@ export { default as Select } from './Select';
27
27
  export { default as Input } from './inputv3';
28
28
  export { default as Checkbox } from './inputv3/Checkbox';
29
29
  export { default as File } from './inputv3/file';
30
- export { default as DateRangeInput } from './inputv3/Date';
30
+ export { default as DateRangeInput } from './inputv3/Date';
31
+ export { default as Rte } from './inputv3/Rte';