5htp-core 0.4.9 → 0.5.0

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 (81) hide show
  1. package/package.json +7 -4
  2. package/src/client/app/component.tsx +2 -2
  3. package/src/client/assets/css/components/button.less +30 -14
  4. package/src/client/assets/css/components/lists.less +3 -3
  5. package/src/client/assets/css/components/other.less +1 -0
  6. package/src/client/assets/css/components/table.less +1 -0
  7. package/src/client/assets/css/text/icons.less +5 -0
  8. package/src/client/assets/css/text/text.less +6 -2
  9. package/src/client/assets/css/theme.less +11 -4
  10. package/src/client/assets/css/utils/borders.less +5 -0
  11. package/src/client/assets/css/utils/layouts.less +4 -1
  12. package/src/client/components/Form.ts +22 -20
  13. package/src/client/components/Select/ChoiceElement.tsx +37 -35
  14. package/src/client/components/Select/ChoiceSelector.tsx +6 -1
  15. package/src/client/components/Select/index.tsx +32 -61
  16. package/src/client/components/Table/index.tsx +24 -79
  17. package/src/client/components/button.tsx +24 -18
  18. package/src/client/components/containers/Popover/getPosition.ts +106 -115
  19. package/src/client/components/containers/Popover/index.tsx +2 -3
  20. package/src/client/components/containers/Popover/popover.less +1 -6
  21. package/src/client/components/dropdown/index.tsx +1 -1
  22. package/src/client/components/input/Slider/index.tsx +0 -5
  23. package/src/client/components/inputv3/Checkbox/index.tsx +0 -6
  24. package/src/client/components/inputv3/Rte/Editor.tsx +17 -9
  25. package/src/client/components/inputv3/Rte/ToolbarPlugin/index.tsx +8 -12
  26. package/src/client/components/inputv3/Rte/index.tsx +2 -8
  27. package/src/client/components/inputv3/Rte/nodes/HeadingNode.ts +55 -0
  28. package/src/client/components/inputv3/Rte/nodes/ImageComponent.tsx +403 -404
  29. package/src/client/components/inputv3/Rte/nodes/ImageNode.tsx +229 -213
  30. package/src/client/components/inputv3/Rte/nodes/PollComponent.tsx +6 -10
  31. package/src/client/components/inputv3/Rte/nodes/PollNode.tsx +3 -3
  32. package/src/client/components/inputv3/Rte/plugins/ComponentPickerPlugin/index.tsx +9 -6
  33. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.css +23 -23
  34. package/src/client/components/inputv3/Rte/plugins/DraggableBlockPlugin/index.tsx +26 -24
  35. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.css +10 -38
  36. package/src/client/components/inputv3/Rte/plugins/FloatingLinkEditorPlugin/index.tsx +349 -356
  37. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.css +80 -84
  38. package/src/client/components/inputv3/Rte/plugins/FloatingTextFormatToolbarPlugin/index.tsx +308 -347
  39. package/src/client/components/inputv3/Rte/plugins/TableActionMenuPlugin/index.tsx +1 -1
  40. package/src/client/components/inputv3/Rte/style.less +0 -2
  41. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.css +0 -33
  42. package/src/client/components/inputv3/Rte/themes/PlaygroundEditorTheme.ts +5 -5
  43. package/src/client/components/inputv3/Rte/ui/DropDown.tsx +1 -1
  44. package/src/client/components/inputv3/base.less +18 -34
  45. package/src/client/components/inputv3/base.tsx +9 -3
  46. package/src/client/components/inputv3/file/index.tsx +50 -48
  47. package/src/client/components/inputv3/index.tsx +1 -7
  48. package/src/client/services/router/components/Link.tsx +7 -1
  49. package/src/client/services/router/index.tsx +3 -3
  50. package/src/client/services/router/request/api.ts +29 -13
  51. package/src/client/services/router/response/index.tsx +2 -4
  52. package/src/client/services/router/response/page.ts +2 -2
  53. package/src/common/data/rte/nodes.ts +9 -1
  54. package/src/common/errors/index.tsx +19 -3
  55. package/src/common/router/index.ts +9 -24
  56. package/src/common/router/request/api.ts +6 -4
  57. package/src/common/router/response/page.ts +19 -7
  58. package/src/common/validation/index.ts +5 -5
  59. package/src/common/validation/schema.ts +13 -22
  60. package/src/common/validation/validator.ts +5 -5
  61. package/src/common/validation/validators.ts +31 -11
  62. package/src/server/app/container/config.ts +8 -1
  63. package/src/server/app/container/console/index.ts +168 -56
  64. package/src/server/app/container/index.ts +4 -12
  65. package/src/server/app/index.ts +1 -1
  66. package/src/server/services/auth/index.ts +1 -0
  67. package/src/server/services/disks/driver.ts +2 -0
  68. package/src/server/services/disks/drivers/s3/index.ts +30 -1
  69. package/src/server/services/email/index.ts +2 -0
  70. package/src/server/services/router/request/index.ts +3 -2
  71. package/src/server/services/router/response/index.ts +1 -2
  72. package/src/server/services/router/response/page/document.tsx +30 -41
  73. package/src/server/services/router/response/page/index.tsx +75 -53
  74. package/src/server/services/schema/index.ts +3 -3
  75. package/src/server/services/schema/request.ts +3 -0
  76. package/src/server/utils/rte.ts +318 -46
  77. package/src/server/utils/slug.ts +67 -0
  78. package/src/client/components/inputv3/Rte/nodes/PlaygroundNodes.ts +0 -76
  79. package/src/server/app/container/console/html.ts +0 -66
  80. package/src/server/services/router/response/page/schemaGenerator.ts +0 -70
  81. package/src/server/services/schema/rte.ts +0 -110
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.9",
4
+ "version": "0.5.0",
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,7 +13,6 @@
13
13
  "framework"
14
14
  ],
15
15
  "dependencies": {
16
- "@excalidraw/excalidraw": "^0.17.6",
17
16
  "@lexical/file": "^0.19.0",
18
17
  "@lexical/headless": "^0.18.0",
19
18
  "@lexical/html": "^0.18.0",
@@ -34,6 +33,7 @@
34
33
  "dayjs": "^1.11.5",
35
34
  "deep-extend": "^0.6.0",
36
35
  "dottie": "^2.0.2",
36
+ "escape-regexp": "^0.0.1",
37
37
  "express": "^4.17.1",
38
38
  "express-csp-header": "^5.0.0",
39
39
  "express-fileupload": "^1.2.1",
@@ -74,7 +74,9 @@
74
74
  "regenerator-runtime": "^0.13.9",
75
75
  "request": "^2.88.2",
76
76
  "sharp": "^0.29.1",
77
+ "slugify": "^1.6.6",
77
78
  "sql-formatter": "^4.0.2",
79
+ "stopword": "^3.1.1",
78
80
  "tslog": "^4.9.1",
79
81
  "uuid": "^8.3.2",
80
82
  "uuid-by-string": "^3.0.4",
@@ -96,9 +98,10 @@
96
98
  "@types/universal-analytics": "^0.4.5",
97
99
  "@types/webpack-env": "^1.16.2",
98
100
  "@types/ws": "^7.4.7",
99
- "@types/yargs-parser": "^21.0.0"
101
+ "@types/yargs-parser": "^21.0.0",
102
+ "schema-dts": "^1.1.2"
100
103
  },
101
104
  "peerDependencies": {
102
- "5htp": "0.3.9"
105
+ "5htp": "0.4.5"
103
106
  }
104
107
  }
@@ -12,7 +12,7 @@ import DialogManager from '@client/components/Dialog/Manager'
12
12
 
13
13
  // Core components
14
14
  import RouterComponent from '@client/services/router/components/router';
15
- import type { TClientOrServerContext } from '@common/router';
15
+ import type { TClientOrServerContextForPage } from '@common/router';
16
16
 
17
17
  // Resources
18
18
  import '@client/assets/css/core.less';
@@ -21,7 +21,7 @@ import '@client/assets/css/core.less';
21
21
  - COMPOSANT
22
22
  ----------------------------------*/
23
23
  export default function App ({ context }: {
24
- context: TClientOrServerContext,
24
+ context: TClientOrServerContextForPage,
25
25
  }) {
26
26
 
27
27
  const curLayout = context.page?.layout;
@@ -11,8 +11,8 @@
11
11
 
12
12
  // Layout
13
13
  position: relative;
14
- gap: @spacing;
15
- min-width: 0/*fit-content*/; // Fit content, even when flexbox parent width < content width
14
+ gap: @spacing / 2;
15
+ min-width: @sizeComponent; // Wdth should be < height
16
16
 
17
17
  // Dimensions
18
18
  font-size: 1em;
@@ -29,8 +29,7 @@
29
29
 
30
30
  // Colors
31
31
  // NOTE: transparent by default (menu items, etc..)
32
- background: transparent;
33
- color: var(--cTxtAccent);
32
+ background: var(--cBg);
34
33
 
35
34
  // Hover
36
35
  //transition: all .5s linear;
@@ -38,6 +37,7 @@
38
37
  li:hover > & {
39
38
 
40
39
  color: var(--cTxtImportant);
40
+ background: var(--cBgActive);
41
41
  //transition: all .1s linear;
42
42
 
43
43
  > i {
@@ -77,9 +77,14 @@
77
77
  }
78
78
  }
79
79
 
80
+ &.selected {
81
+ background: var(--cBgSelected);
82
+ color: #fff;
83
+ }
84
+
80
85
  // Click
81
86
  &.pressed {
82
- transform: scale(0.9);
87
+ background: var(--cBgPressed) !important;
83
88
  }
84
89
 
85
90
  &,
@@ -149,6 +154,11 @@
149
154
 
150
155
  }
151
156
 
157
+ &:not(.bg) {
158
+
159
+ color: var(--cTxtAccent);
160
+ }
161
+
152
162
  /*----------------------------------
153
163
  - STATE
154
164
  ----------------------------------*/
@@ -231,7 +241,7 @@
231
241
  // Give less imortance to buttons which are in lists
232
242
  color: var(--cTxtBase);
233
243
  box-shadow: none;
234
- //width: 100%;
244
+ padding: 0 1em; // Row display = more condensed
235
245
 
236
246
  &.active,
237
247
  &:hover {
@@ -239,14 +249,6 @@
239
249
  color: var(--cTxtImportant);
240
250
  }
241
251
 
242
- > .label {
243
- // All the list items label must be aligned
244
- justify-content: flex-start;
245
- // Since they're all horizontally aligned,
246
- // Label = max width, so icon right are also aligned to right
247
- flex: 1;
248
- }
249
-
250
252
  &.icon {
251
253
 
252
254
  color: var(--cTxtDesc);
@@ -273,6 +275,20 @@
273
275
  }
274
276
  }
275
277
 
278
+ .col {
279
+ &.menu > .btn,
280
+ &.menu > li > .btn {
281
+
282
+ > .label {
283
+ // All the list items label must be aligned
284
+ justify-content: flex-start;
285
+ // Since they're all horizontally aligned,
286
+ // Label = max width, so icon right are also aligned to right
287
+ flex: 1;
288
+ }
289
+ }
290
+ }
291
+
276
292
  ul.col,
277
293
  ul.row {
278
294
 
@@ -94,9 +94,9 @@ ol.steps {
94
94
  // Stepnumber = at the left
95
95
  &:not(.col) {
96
96
 
97
- padding-left: /*@sizeStep + */(@spacing);
97
+ padding-left: /*@sizeStep + */1.5em;
98
98
  padding-top: (@sizeStep - @itemLineHeight) / 2;
99
- line-height: @itemLineHeight;
99
+ //line-height: @itemLineHeight;
100
100
  margin: 5px 0;
101
101
 
102
102
  &:before {
@@ -124,7 +124,7 @@ strong.step {
124
124
  font-weight: bold;
125
125
 
126
126
  font-size: 1em;
127
- line-height: 1.8em;
127
+ line-height: 2.2em;
128
128
  }
129
129
 
130
130
  /*----------------------------------
@@ -131,6 +131,7 @@ strong.placeholder {
131
131
  hr {
132
132
 
133
133
  border: none;
134
+ text-align: center;
134
135
 
135
136
  &::before {
136
137
  display: block;
@@ -32,6 +32,7 @@ table {
32
32
  th {
33
33
  font-weight: 500;
34
34
  color: var(--cTxtDesc);
35
+ white-space: break-spaces;
35
36
  }
36
37
 
37
38
  td, th {
@@ -15,6 +15,11 @@ i {
15
15
  text-align: center;
16
16
  font-style: normal;
17
17
 
18
+ // Avoid the flexbox to crush the icon
19
+ .row > & {
20
+ flex: 0 0 1em;
21
+ }
22
+
18
23
  &.spin {
19
24
  animation-name: spin;
20
25
  animation-iteration-count: infinite;
@@ -26,6 +26,10 @@ a {
26
26
  position: relative;
27
27
  }
28
28
  }
29
+
30
+ &.discrete:not(:hover) {
31
+ text-decoration: none;
32
+ }
29
33
  }
30
34
 
31
35
  [contenteditable]:focus {
@@ -182,7 +186,7 @@ pre {
182
186
  }
183
187
 
184
188
  code {
185
- padding: @spacing @readingMargin;
189
+ //padding: @spacing @readingMargin;
186
190
  text-align: left;
187
191
  font-size: 1rem;
188
192
  line-height: 2em;
@@ -206,7 +210,7 @@ pre {
206
210
  }
207
211
 
208
212
  figcaption {
209
- margin-top: @spacing;
213
+ margin-top: @spacing / 2;
210
214
  font-style: italic;
211
215
  text-align: center;
212
216
  }
@@ -19,9 +19,7 @@
19
19
  // Default theming for the children components
20
20
  @{componentsSelector} {
21
21
  // Don't apply the default theme to already themed components
22
- &:not(.bg) {
23
- .apply-theme-style( @componentsTheme, false, false );
24
- }
22
+ .apply-theme-style( @componentsTheme, false, false );
25
23
  }
26
24
  }
27
25
  }
@@ -35,15 +33,23 @@
35
33
  @bgActive: if( (@theme[alpha]) or (alpha(@bg) < 1),
36
34
  fadeout( @bg, 90%),
37
35
  if( @isLight,
38
- @bg - #040404,
36
+ @bg - #0A0A0A,
39
37
  @bg + #111,
40
38
  )
41
39
  );
40
+ @bgPressed: if( (@theme[alpha]) or (alpha(@bg) < 1),
41
+ fadeout( @bg, 80%),
42
+ if( @isLight,
43
+ @bg - #1A1A1A,
44
+ @bg + #222,
45
+ )
46
+ );
42
47
  @fg: @theme[foreground];
43
48
 
44
49
  // Background
45
50
  --cBg: @bg;
46
51
  --cBgActive: @bgActive;
52
+ --cBgPressed: @bgPressed;
47
53
  & when (@apply = true) {
48
54
  background: var(--cBg);
49
55
  }
@@ -51,6 +57,7 @@
51
57
  // Accent
52
58
  & when (@theme[accent1]) {
53
59
  --cTxtAccent: @theme[accent1];
60
+ --cBgSelected: @@theme[accent1];
54
61
  }
55
62
  & when (@theme[accent2]) {
56
63
  --cTxtAccent2: @theme[accent2];
@@ -6,5 +6,10 @@
6
6
  .bb-1, .bv-1 { border-bottom: solid 1px var(--cLine); }
7
7
  .bl-1, .bh-1 { border-left: solid 1px var(--cLine); }
8
8
 
9
+ .bt-2, .bv-2 { border-top: solid 2px var(--cLine); }
10
+ .br-2, .bh-2 { border-right: solid 2px var(--cLine); }
11
+ .bb-2, .bv-2 { border-bottom: solid 2px var(--cLine); }
12
+ .bl-2, .bh-2 { border-left: solid 1px var(--cLine); }
13
+
9
14
  .b-0 { border: none; }
10
15
  .b-1 { border-top: solid 1px var(--cLine); }
@@ -140,8 +140,11 @@
140
140
  }
141
141
  }
142
142
 
143
+ // Put this one at first because high possibilities we need to override
144
+ &.al-middle,
145
+ &.al-center { justify-content: center; }
146
+
143
147
  &, &.al-top { justify-content: flex-start; }
144
- &.al-middle, &.al-center { justify-content: center; }
145
148
  &.al-bottom { justify-content: flex-end; }
146
149
 
147
150
  &.al-left { align-items: flex-start; }
@@ -17,7 +17,7 @@ export type { TValidationResult, TSchemaData } from '@common/validation/schema';
17
17
  /*----------------------------------
18
18
  - TYPES
19
19
  ----------------------------------*/
20
- type TFormOptions<TFormData extends {}> = {
20
+ export type TFormOptions<TFormData extends {}> = {
21
21
  data?: Partial<TFormData>,
22
22
  submit?: (data: TFormData) => Promise<void>,
23
23
  autoValidateOnly?: (keyof TFormData)[],
@@ -128,36 +128,38 @@ export default function useForm<TFormData extends {}>(
128
128
  return validated;
129
129
  }
130
130
 
131
- const submit = async (additionnalData: Partial<TFormData> = {}) => {
131
+ const submit = (additionnalData: Partial<TFormData> = {}) => {
132
132
 
133
133
  const allData = { ...data, ...additionnalData }
134
134
 
135
135
  // Validation
136
136
  const validated = validate(allData);
137
137
  if (validated.errorsCount !== 0) {
138
- context.app.handleError(
139
- new InputErrorSchema(validated.erreurs)
140
- );
141
- console.log("validated", validated.erreurs);
142
- return;
138
+ throw new InputErrorSchema(validated.erreurs);
143
139
  }
144
140
 
145
- // Callback
146
- let submitResult: any;
147
- if (options.submit)
148
- submitResult = await options.submit(allData as TFormData);
141
+ const afterSubmit = (responseData?: any) => {
149
142
 
150
- // Reset autosaved data
151
- if (options.autoSave)
152
- localStorage.removeItem('form.' + options.autoSave.id);
143
+ // Reset autosaved data
144
+ if (options.autoSave)
145
+ localStorage.removeItem('form.' + options.autoSave.id);
146
+
147
+ // Update state
148
+ setState( current => ({
149
+ ...current,
150
+ hasChanged: false
151
+ }));
153
152
 
154
- // Update state
155
- setState( current => ({
156
- ...current,
157
- hasChanged: false
158
- }));
153
+ return responseData;
154
+ }
159
155
 
160
- return submitResult;
156
+ // Callback
157
+ if (options.submit)
158
+ return options.submit(allData as TFormData).then(afterSubmit);
159
+ else {
160
+ afterSubmit();
161
+ return undefined;
162
+ }
161
163
  }
162
164
 
163
165
  const rebuildFieldsAttrs = (newState: Partial<FormState> = {}) => {
@@ -20,7 +20,7 @@ import type { Props } from '.';
20
20
  /*----------------------------------
21
21
  - COMPONENT
22
22
  ----------------------------------*/
23
- export default ({ choice, currentList, onChange, multiple, includeCurrent, format = 'badge' }: {
23
+ export default ({ choice, currentList, onChange, multiple, required, includeCurrent, format = 'badge' }: {
24
24
  choice: Choice,
25
25
  currentList: Choice[],
26
26
  includeCurrent?: boolean,
@@ -30,48 +30,50 @@ export default ({ choice, currentList, onChange, multiple, includeCurrent, forma
30
30
  const isCurrent = currentList.some(c => c.value === choice.value);
31
31
  if (isCurrent && !includeCurrent) return null;
32
32
 
33
- const showRemoveButton = multiple;
33
+ const canUnselect = !required || currentList.length > 1;
34
+
35
+ const onClick = () => {
36
+
37
+ if (isCurrent && !canUnselect)
38
+ return;
39
+
40
+ onChange( current => multiple
41
+ ? (isCurrent
42
+ ? currentList.filter(item => item.value !== choice.value)
43
+ : [...(current || []), choice]
44
+ )
45
+ : isCurrent ? undefined : choice
46
+ );
47
+ }
48
+
49
+ const btnProps = {
50
+ selected: isCurrent,
51
+ onClick,
52
+ icon: choice.color ? (
53
+ <span class="pastille" style={{ background: '#' + choice.color }} />
54
+ ) : choice.icon ? (
55
+ <i src={choice.icon} />
56
+ ) : undefined
57
+ }
34
58
 
35
59
  return format === 'list' ? (
36
60
  <li>
37
- <Button icon={isCurrent ? 'check-circle' : undefined} onClick={() => onChange( current => multiple
38
- ? (isCurrent
39
- ? currentList.filter(item => item.value !== choice.value)
40
- : [...(current || []), choice]
41
- )
42
- : isCurrent ? undefined : choice
43
- )}>
61
+ <Button {...btnProps}>
44
62
  {choice.label}
45
63
  </Button>
46
64
  </li>
47
- ) : isCurrent ? (
48
- <li class={"badge bg primary"+ (showRemoveButton ? ' pdr-05' : '')}>
49
- {choice.label}
50
-
51
- {showRemoveButton && (
52
- <span class="badge xs clickable" onClick={(e) => {
53
- e.stopPropagation();
54
- onChange( current => current.filter( c => c.value !== choice.value))
55
- return false;
56
- }}>
57
- x
58
- </span>
59
- )}
60
- </li>
61
65
  ) : (
62
- <li class={"badge clickable"} onClick={() => {
63
- onChange( current => multiple
64
- ? [...(current || []), choice]
65
- : choice
66
- );
67
- }}>
68
- {/*search.keywords ? (
69
- <span>
70
-
71
- <strong>{search.keywords}</strong>{choice.label.slice( search.keywords.length )}
66
+ <li>
67
+ <Button type="secondary" {...btnProps}>
72
68
 
73
- </span>
74
- ) : */choice.label}
69
+ {choice.label}
70
+
71
+ {(isCurrent && canUnselect) && (
72
+ <span class="badge xs clickable">
73
+ x
74
+ </span>
75
+ )}
76
+ </Button>
75
77
  </li>
76
78
  )
77
79
  }
@@ -16,7 +16,12 @@ import type { TDialogControls } from '@client/components/dropdown';
16
16
  - TYPES
17
17
  ----------------------------------*/
18
18
 
19
- export type Choice = { label: ComponentChild, value: string }
19
+ export type Choice = {
20
+ label: ComponentChild,
21
+ value: string,
22
+ color?: string,
23
+ icon?: string,
24
+ }
20
25
 
21
26
  type ChoicesFunc = (search: string) => Promise<Choice[]>
22
27
 
@@ -165,6 +165,7 @@ export default (props: Props) => {
165
165
  ----------------------------------*/
166
166
 
167
167
  const selectedItems = enableSearch ? currentList : []
168
+ const selectedItemsValue = selectedItems.map( c => c.value);
168
169
 
169
170
  const Search = enableSearch && (
170
171
  <Input
@@ -197,6 +198,7 @@ export default (props: Props) => {
197
198
  currentList={currentList}
198
199
  onChange={onChange}
199
200
  multiple={multiple}
201
+ required={required}
200
202
  includeCurrent
201
203
  />
202
204
  ))}
@@ -214,6 +216,7 @@ export default (props: Props) => {
214
216
  currentList={currentList}
215
217
  onChange={onChange}
216
218
  multiple={multiple}
219
+ required={required}
217
220
  includeCurrent
218
221
  />
219
222
  ))}
@@ -229,7 +232,7 @@ export default (props: Props) => {
229
232
  )}
230
233
  </div>
231
234
  )} state={popoverState}>
232
- <Button type="secondary" icon={icon} iconR="chevron-down" {...otherProps}>
235
+ <Button icon={icon} iconR="angle-down" {...otherProps}>
233
236
 
234
237
  {currentList.length === 0 ? <>
235
238
  {title}
@@ -239,69 +242,37 @@ export default (props: Props) => {
239
242
  {currentList[0].label}
240
243
  </>}
241
244
 
242
- {errors?.length && (
243
- <div class="bubble bg error bottom">
244
- {errors.join('. ')}
245
- </div>
246
- )}
247
-
248
245
  </Button>
249
246
  </Popover>
250
247
  ) : (
251
- <div class="col sp-05">
252
- <div class={className} onMouseDown={() => refInputSearch.current?.focus()}>
253
-
254
- <div class="row al-left wrap pd-1">
255
-
256
- {icon !== undefined && (
257
- <i src={icon} />
258
- )}
259
-
260
- <div class="col al-left sp-05">
261
-
262
- <label>{title}{isRequired && (
263
- <span class="fg error">&nbsp;*</span>
264
- )}</label>
265
-
266
- <div class="row al-left wrap sp-05">
267
-
268
- {selectedItems.map( choice => (
269
- <ChoiceElement format='badge' choice={choice}
270
- currentList={currentList}
271
- onChange={onChange}
272
- multiple={multiple}
273
- includeCurrent
274
- />
275
- ))}
276
-
277
- {Search}
278
- </div>
279
- </div>
280
-
281
- </div>
282
-
283
- <div class="pd-1">
284
- <ul class="row al-left wrap sp-05" style={{
285
- maxHeight: '30vh',
286
- overflowY: 'auto'
287
- }}>
288
- {choices.map( choice => (
289
- <ChoiceElement format='badge' choice={choice}
290
- currentList={currentList}
291
- onChange={onChange}
292
- multiple={multiple}
293
- includeCurrent
294
- />
295
- ))}
296
- </ul>
297
- </div>
298
-
299
- </div>
300
- {errors?.length && (
301
- <div class="bubble bg error bottom">
302
- {errors.join('. ')}
303
- </div>
304
- )}
248
+ <div class={className} onMouseDown={() => refInputSearch.current?.focus()}>
249
+
250
+ {Search}
251
+
252
+ <ul class="row al-left wrap sp-05 scrollable mgt-1" style={{
253
+ maxHeight: '30vh',
254
+ }}>
255
+ {selectedItems.map( choice => (
256
+ <ChoiceElement format='badge' choice={choice}
257
+ currentList={currentList}
258
+ onChange={onChange}
259
+ multiple={multiple}
260
+ required={required}
261
+ includeCurrent
262
+ />
263
+ ))}
264
+
265
+ {choices.map( choice => !selectedItemsValue.includes(choice.value) && (
266
+ <ChoiceElement format='badge' choice={choice}
267
+ currentList={currentList}
268
+ onChange={onChange}
269
+ multiple={multiple}
270
+ required={required}
271
+ includeCurrent
272
+ />
273
+ ))}
274
+ </ul>
275
+
305
276
  </div>
306
277
  )}
307
278
  </InputWrapper>