5htp-core 0.4.7 → 0.4.8-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 (40) hide show
  1. package/package.json +6 -1
  2. package/src/client/assets/css/components/table.less +2 -0
  3. package/src/client/assets/css/text/titres.less +1 -1
  4. package/src/client/assets/css/utils/layouts.less +8 -2
  5. package/src/client/components/Dialog/Manager.tsx +65 -29
  6. package/src/client/components/Dialog/card.tsx +3 -1
  7. package/src/client/components/Dialog/index.less +1 -2
  8. package/src/client/components/Select/ChoiceSelector.tsx +7 -5
  9. package/src/client/components/Select/index.tsx +162 -130
  10. package/src/client/components/index.ts +2 -1
  11. package/src/client/components/inputv3/Rte/ExampleTheme.tsx +42 -0
  12. package/src/client/components/inputv3/Rte/ToolbarPlugin.tsx +167 -0
  13. package/src/client/components/inputv3/Rte/icons/LICENSE.md +5 -0
  14. package/src/client/components/inputv3/Rte/icons/arrow-clockwise.svg +4 -0
  15. package/src/client/components/inputv3/Rte/icons/arrow-counterclockwise.svg +4 -0
  16. package/src/client/components/inputv3/Rte/icons/journal-text.svg +5 -0
  17. package/src/client/components/inputv3/Rte/icons/justify.svg +3 -0
  18. package/src/client/components/inputv3/Rte/icons/text-center.svg +3 -0
  19. package/src/client/components/inputv3/Rte/icons/text-left.svg +3 -0
  20. package/src/client/components/inputv3/Rte/icons/text-paragraph.svg +3 -0
  21. package/src/client/components/inputv3/Rte/icons/text-right.svg +3 -0
  22. package/src/client/components/inputv3/Rte/icons/type-bold.svg +3 -0
  23. package/src/client/components/inputv3/Rte/icons/type-italic.svg +3 -0
  24. package/src/client/components/inputv3/Rte/icons/type-strikethrough.svg +3 -0
  25. package/src/client/components/inputv3/Rte/icons/type-underline.svg +3 -0
  26. package/src/client/components/inputv3/Rte/index.tsx +163 -0
  27. package/src/client/components/inputv3/Rte/style.less +428 -0
  28. package/src/client/components/inputv3/base.less +20 -33
  29. package/src/client/components/inputv3/base.tsx +36 -2
  30. package/src/client/components/inputv3/file/index.tsx +11 -5
  31. package/src/client/components/inputv3/index.tsx +45 -44
  32. package/src/common/data/rte/index.ts +66 -0
  33. package/src/common/data/rte/nodes.ts +20 -0
  34. package/src/common/validation/validators.ts +49 -4
  35. package/src/server/services/auth/index.ts +0 -2
  36. package/src/server/services/database/index.ts +1 -1
  37. package/src/client/components/input/Rte/index.less +0 -13
  38. package/src/client/components/input/Rte/index.tsx +0 -143
  39. package/src/client/components/input/Rte/selection.ts +0 -34
  40. 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.7",
4
+ "version": "0.4.8-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",
@@ -88,6 +88,8 @@ table {
88
88
  td {
89
89
 
90
90
  cursor: default;
91
+ max-width: 25em;
92
+ overflow: hidden;
91
93
 
92
94
  &.extendable > .row {
93
95
 
@@ -3,7 +3,7 @@ h2,
3
3
  h3 {
4
4
 
5
5
  font-weight: 600;
6
- text-align: inherit;
6
+ text-align: left; // Align all titles to the left by default
7
7
  color: var(--cTxtImportant);
8
8
 
9
9
  /*.img & {
@@ -57,7 +57,10 @@
57
57
  justify-content: flex-start;
58
58
  }
59
59
 
60
- &.sep-1 > * + * { border-left: solid 1px var(--cLine); }
60
+ &.sep-1 {
61
+ gap: 0;
62
+ > * + * { border-left: solid 1px var(--cLine); }
63
+ }
61
64
 
62
65
  &.menu {
63
66
 
@@ -146,7 +149,10 @@
146
149
  &.al-right { align-items: flex-end; }
147
150
  &, &.al-fill { align-items: stretch; }
148
151
 
149
- &.sep-1 > * + * { border-top: solid 1px var(--cLine); }
152
+ &.sep-1 {
153
+ gap: 0;
154
+ > * + * { border-top: solid 1px var(--cLine); }
155
+ }
150
156
 
151
157
  > .col-1 { align-self: stretch; }
152
158
 
@@ -27,7 +27,11 @@ type TParams = { [cle: string]: unknown }
27
27
 
28
28
  type ComposantToast = React.FunctionComponent<{ close?: any }> & { data?: object };
29
29
 
30
- type TOptsToast = (CardInfos & { content?: ComponentChild })
30
+ type TOptsToast = (CardInfos & {
31
+ content?: ComponentChild,
32
+ data?: {},
33
+ className?: string,
34
+ })
31
35
 
32
36
  type TOnCloseCallback<TReturnType extends any> = (returnedValue: TReturnType) => void
33
37
 
@@ -43,16 +47,25 @@ export type TDialogControls = {
43
47
  then: (cb: TOnCloseCallback<any>) => any
44
48
  }
45
49
 
50
+ type TDialogContentArg = ComposantToast | Promise<{ default: ComposantToast }> | TOptsToast;
51
+
52
+ type TDialogShowArgs = [
53
+ // On utilise une fonction pour pouvoir accéder aux fonctions (close, ...) lors de la déclaration des infos de la toast
54
+ Content: TDialogContentArg,
55
+ paramsInit?: TParams
56
+ ] | [
57
+ title: string,
58
+ // On utilise une fonction pour pouvoir accéder aux fonctions (close, ...) lors de la déclaration des infos de la toast
59
+ Content: TDialogContentArg,
60
+ paramsInit?: TParams
61
+ ]
62
+
46
63
  type DialogActions = {
47
64
 
48
65
  setToasts: ( setter: (old: ComponentChild[]) => ComponentChild[]) => void,
49
66
  setModals: ( setter: (old: ComponentChild[]) => ComponentChild[]) => void,
50
67
 
51
- show: (
52
- // On utilise une fonction pour pouvoir accéder aux fonctions (close, ...) lors de la déclaration des infos de la toast
53
- Content: ComposantToast | Promise<{ default: ComposantToast }> | TOptsToast,
54
- paramsInit?: TParams
55
- ) => TDialogControls,
68
+ show: (...args: TDialogShowArgs ) => TDialogControls,
56
69
 
57
70
  confirm: (title: string, content: string | ComponentChild, defaultBtn: 'Yes'|'No') => TDialogControls,
58
71
 
@@ -73,26 +86,33 @@ type DialogActions = {
73
86
  let idA: number = 0;
74
87
  export const createDialog = (app: Application, isToast: boolean): DialogActions => {
75
88
 
76
- const show = <TReturnType extends any = true>(
77
- // On utilise une fonction pour pouvoir accéder aux fonctions (close, ...) lors de la déclaration des infos de la toast
78
- Content: ComposantToast | Promise<{ default: ComposantToast }> | TOptsToast,
79
- paramsInit?: TParams
80
- ): TDialogControls => {
89
+ const show = <TReturnType extends any = true>( ...args: TDialogShowArgs ): TDialogControls => {
81
90
 
82
91
  let onClose: TOnCloseCallback<TReturnType>;
83
92
  const id = idA++;
84
93
 
94
+ // Parse args
95
+ let title: string | undefined;
96
+ let Content: TDialogContentArg;
97
+ let paramsInit: TParams = {};
98
+ if (typeof args[0] === 'string') {
99
+ [title, Content, paramsInit] = args;
100
+ } else {
101
+ [Content, paramsInit] = args;
102
+ }
103
+
104
+ // Set instance management function
85
105
  const setDialog = isToast
86
106
  ? instance.setToasts
87
107
  : instance.setModals;
88
108
 
109
+ // Close function
89
110
  const close = (retour: TReturnType) => {
90
111
 
91
112
  setDialog(q => q.filter(m => m.id !== id))
92
113
 
93
114
  if (onClose !== undefined)
94
115
  onClose(retour);
95
-
96
116
  };
97
117
 
98
118
  const promise = new Promise(async (resolve: TOnCloseCallback<TReturnType>) => {
@@ -101,33 +121,49 @@ export const createDialog = (app: Application, isToast: boolean): DialogActions
101
121
  let render: ComponentChild;
102
122
  let propsRendu: CardInfos = {
103
123
  ...paramsInit,
104
- close: close,
105
- data: {}
106
- };
124
+ close: close
125
+ };
126
+
127
+ // modal.show( import('./modalSupprimer') )
128
+ // -> Fetch component
129
+ if (Content.constructor === Promise)
130
+ Content = (await Content).default;
131
+
132
+ // modal.show('Supprimer', import('./modalSupprimer'))
133
+ // -> Shortcut for modal.show({ title: 'Suoorimer', content: <Component> })
134
+ if (title !== undefined) {
135
+ Content = {
136
+ title: title,
137
+ content: Content
138
+ }
139
+ }
107
140
 
108
- // toast.show({ title: 'supprimer', content: <>...</> })
141
+ // modal.show({ title: 'supprimer', content: <>...</> })
109
142
  if (Content.constructor === Object) {
110
143
 
111
- const { content, ...propsToast } = Content as TOptsToast;
144
+ const { content: CardContent, data = {}, ...propsToast } = Content as TOptsToast;
145
+
146
+ let cardContent: ComponentChild;
147
+ if (typeof CardContent === 'function') {
148
+ cardContent = <CardContent {...propsRendu} {...data} />
149
+ propsToast.boutons = null; // Component content = advanced content = should include buttons
150
+ } else {
151
+ cardContent = CardContent;
152
+ }
153
+
112
154
  render = (
113
- <Card {...propsRendu} children={content} {...propsToast} isToast={isToast} />
155
+ <Card {...propsRendu} {...propsToast} isToast={isToast}>
156
+ {cardContent}
157
+ </Card>
114
158
  )
115
159
 
116
- // toast.show( import('./modalSupprimer') )
117
- // toast.show( ToastSupprimer )
160
+ // modal.show( ToastSupprimer )
161
+ // -> Content is a component rendering a Card
118
162
  } else {
119
163
 
120
- let DialogCard;
121
- if (Content.constructor === Promise) {
122
- DialogCard = (await Content).default;
123
- } else {
124
- DialogCard = Content as typeof Card;
125
- }
126
-
127
164
  render = (
128
- <DialogCard {...propsRendu} isToast={isToast} />
165
+ <Content {...propsRendu} isToast={isToast} />
129
166
  )
130
-
131
167
  }
132
168
 
133
169
  // Chargeur de données
@@ -27,6 +27,7 @@ export type Props = {
27
27
  cover?: string,
28
28
  icon?: ComponentChild,
29
29
  title?: string | ComponentChild,
30
+ className?: string,
30
31
 
31
32
  children?: ComponentChild,
32
33
  isToast?: boolean,
@@ -49,6 +50,7 @@ export default ({
49
50
  cover,
50
51
  icon,
51
52
  title,
53
+ className = '',
52
54
 
53
55
  children,
54
56
  isToast,
@@ -149,7 +151,7 @@ export default ({
149
151
 
150
152
  </div>
151
153
  ) : (
152
- <div class={"card pd-2 col al-top"} style={width === undefined
154
+ <div class={"card pd-2 col al-top " + className} style={width === undefined
153
155
  ? {}
154
156
  : { minWidth: width + "px", maxWidth: width + "px" }
155
157
  }>
@@ -78,9 +78,8 @@
78
78
  > .card {
79
79
 
80
80
  position: relative;
81
- max-height: 80vh;
82
81
  min-width: 300px;
83
- max-height: 100vh;
82
+ max-height: 90vh;
84
83
  box-shadow: none;
85
84
  overflow-y: auto;
86
85
 
@@ -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,14 @@ 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
+ addNew?: (search?: string) => Promise<Choice>
43
+ }
44
+
45
+ type SelectorProps = Props & {
44
46
  currentList: Choice[],
45
47
  refDropdown?: RefObject<TDialogControls>
46
48
  }
@@ -67,7 +69,7 @@ export default React.forwardRef<HTMLDivElement, Props>(({
67
69
  currentList,
68
70
  refDropdown,
69
71
  ...otherProps
70
- }: Props, ref) => {
72
+ }: SelectorProps, ref) => {
71
73
 
72
74
 
73
75
 
@@ -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,28 @@ 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
+ addNew,
73
+ ...otherProps
74
+ } = props;
72
75
 
73
76
  /*----------------------------------
74
77
  - INIT
@@ -77,8 +80,10 @@ export default ({
77
80
  const popoverState = React.useState(false);
78
81
 
79
82
  const choicesViaFunc = typeof initChoices === 'function';
80
- if (choicesViaFunc && enableSearch === undefined)
83
+ if (choicesViaFunc)
81
84
  enableSearch = true;
85
+ else if (typeof initChoices[0] === 'string')
86
+ initChoices = initChoices.map( c => ({ label: c, value: c }));
82
87
 
83
88
  const refInputSearch = React.useRef<HTMLInputElement | null>(null);
84
89
 
@@ -144,6 +149,17 @@ export default ({
144
149
  }
145
150
  }
146
151
 
152
+ const clickAddNew = () => addNew(search.keywords).then( newItem => {
153
+
154
+ if (!newItem)
155
+ return;
156
+
157
+ onChange( current => multiple
158
+ ? [...(current || []), newItem]
159
+ : newItem
160
+ )
161
+ })
162
+
147
163
  /*----------------------------------
148
164
  - RENDER
149
165
  ----------------------------------*/
@@ -152,126 +168,142 @@ export default ({
152
168
 
153
169
  const Search = enableSearch && (
154
170
  <Input
171
+ icon="search"
155
172
  placeholder="Type your search here"
156
173
  value={search.keywords}
157
174
  onChange={keywords => setSearch(s => ({ ...s, loading: true, keywords }))}
158
175
  inputRef={refInputSearch}
176
+ wrapper={false}
177
+ className="bg white"
159
178
  />
160
179
  )
161
180
 
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" />
186
- </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
- )}
181
+ return (
182
+ <InputWrapper {...props}>
183
+ {dropdown ? (
184
+ <Popover {...(dropdown === true ? {
185
+ width: '200px',
186
+ } : dropdown)} content={(
187
+ <div class="bg white col al-top">
229
188
 
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}
189
+ <div class="bb-1" style={{ position: 'sticky', top: 0, zIndex: 5 }}>
190
+ {Search}
248
191
  </div>
192
+
193
+ {(multiple && selectedItems.length !== 0) && (
194
+ <ul class="row al-left wrap sp-05">
195
+ {selectedItems.map( choice => (
196
+ <ChoiceElement format='badge' choice={choice}
197
+ currentList={currentList}
198
+ onChange={onChange}
199
+ multiple={multiple}
200
+ includeCurrent
201
+ />
202
+ ))}
203
+ </ul>
204
+ )}
205
+
206
+ {search.loading ? (
207
+ <div class="row al-center h-2">
208
+ <i src="spin" />
209
+ </div>
210
+ ) : (
211
+ <ul class="menu col">
212
+ {choices.map( choice => (
213
+ <ChoiceElement format='list' choice={choice}
214
+ currentList={currentList}
215
+ onChange={onChange}
216
+ multiple={multiple}
217
+ includeCurrent
218
+ />
219
+ ))}
220
+ </ul>
221
+ )}
222
+
223
+ {addNew && (
224
+ <div class="col" style={{ position: 'sticky', bottom: '0px', zIndex: 5 }}>
225
+ <Button type="primary" icon="plus" onClick={clickAddNew}>
226
+ Add new
227
+ </Button>
228
+ </div>
229
+ )}
249
230
  </div>
231
+ )} state={popoverState}>
232
+ <Button type="secondary" icon={icon} iconR="chevron-down" {...otherProps}>
233
+
234
+ {currentList.length === 0 ? <>
235
+ {title}
236
+ </> : multiple ? <>
237
+ {title} <span class="badge s bg accent">{currentList.length}</span>
238
+ </> : <>
239
+ {currentList[0].label}
240
+ </>}
241
+
242
+ {errors?.length && (
243
+ <div class="bubble bg error bottom">
244
+ {errors.join('. ')}
245
+ </div>
246
+ )}
247
+
248
+ </Button>
249
+ </Popover>
250
+ ) : (
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>
250
280
 
251
- </div>
281
+ </div>
252
282
 
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('. ')}
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
+ )}
273
305
  </div>
274
306
  )}
275
- </div>
307
+ </InputWrapper>
276
308
  )
277
309
  }
@@ -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';