5htp-core 0.2.5-2 → 0.2.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.
@@ -4,182 +4,71 @@
4
4
 
5
5
  // Npm
6
6
  import React from 'react';
7
- import type { ComponentChild } from 'preact';
8
- import type { StateUpdater } from 'preact/hooks';
9
7
 
10
8
  // Core
11
- import Button from '@client/components/button';
12
- import String from '@client/components/inputv3/string';
13
9
  import Dropdown, { TDialogControls, Props as DropdownProps } from '@client/components/dropdown';
14
10
 
15
- /*----------------------------------
16
- - CONST
17
- ----------------------------------*/
11
+ // Specific
12
+ import ChoiceSelector, {
13
+ Props as SelectorProps,
14
+ Choice,
15
+ } from './ChoiceSelector';
18
16
 
19
17
  /*----------------------------------
20
18
  - TYPES
21
19
  ----------------------------------*/
22
20
 
23
- export type Choice = { label: ComponentChild, value: string }
24
-
25
- export type Choices = Choice[]
26
-
27
- type ChoicesFunc = (search: string) => Promise<Choices>
28
-
29
- type SelectorProps = (
30
- {
31
- multiple: true,
32
- value?: Choice[],
33
- onChange: StateUpdater<Choice[]>,
34
- validator?: ArrayValidator
35
- }
36
- |
37
- {
38
- multiple?: false,
39
- value?: Choice,
40
- onChange: StateUpdater<Choice>,
41
- validator?: StringValidator
42
- }
43
- ) & {
44
- choices: Choices | ChoicesFunc,
45
- enableSearch?: boolean,
46
- inline?: boolean,
47
- errors?: string[],
48
- required?: boolean,
49
- noneSelection?: false | string
50
- }
51
-
52
21
  export type Props = DropdownProps & SelectorProps & {
53
22
  title: string,
23
+ errors?: string[],
54
24
  }
55
25
 
56
26
  /*----------------------------------
57
27
  - COMONENT
58
28
  ----------------------------------*/
59
29
  export default ({
60
- title, choices: initChoices, errors, validator, required, noneSelection, enableSearch, value: current,
61
- onChange, inline, multiple,
30
+ title,
31
+ errors,
62
32
  ...props
63
33
  }: Props) => {
64
34
 
65
- const refModal = React.useRef<TDialogControls>(null);
66
-
67
35
  /*----------------------------------
68
36
  - INIT
69
37
  ----------------------------------*/
70
38
 
71
- const choicesViaFunc = typeof initChoices === 'function';
72
- if (choicesViaFunc && enableSearch === undefined)
73
- enableSearch = true;
74
-
75
- const [search, setSearch] = React.useState<{
76
- keywords: string,
77
- loading: boolean
78
- }>({
79
- keywords: '',
80
- loading: choicesViaFunc
81
- });
82
-
83
- const [choices, setChoices] = React.useState<Choices>([]);
39
+ const refModal = React.useRef<TDialogControls>(null);
84
40
 
85
41
  /*----------------------------------
86
42
  - ACTIONS
87
43
  ----------------------------------*/
88
- React.useEffect(() => {
89
- if (choicesViaFunc) {
90
- initChoices(search.keywords).then((searchResults) => {
91
- setSearch(s => ({ ...s, loading: false }))
92
- setChoices(searchResults);
93
- })
94
- }
95
- }, [initChoices, search.keywords]);
96
44
 
97
- const currentList: Choice[] = current === undefined
45
+ const currentList: Choice[] = props.value === undefined
98
46
  ? []
99
- : (Array.isArray(current) ? current : [current]);
47
+ : (Array.isArray(props.value)
48
+ ? props.value
49
+ : [props.value]
50
+ );
100
51
 
101
52
  /*----------------------------------
102
53
  - RENDER
103
54
  ----------------------------------*/
104
-
105
- const selector = (
106
- <div class={(inline ? '' : 'card ') + "col"}>
107
-
108
- {enableSearch && (
109
- <String icon="search"
110
- title="Search"
111
- value={search.keywords}
112
- onChange={keywords => setSearch(s => ({ ...s, loading: true, keywords }))}
113
- iconR={'spin'}
114
- />
115
- )}
116
-
117
- {currentList.length !== 0 && (
118
- <ul class="col menu">
119
- {currentList.map(choice => (
120
- <Button size="s" onClick={() => {
121
- onChange( current => multiple
122
- ? current.filter(c => c.value !== choice.value)
123
- : undefined
124
- );
125
- }} suffix={<i src="check" class="fg primary" />}>
126
- {choice.label}
127
- </Button>
128
- ))}
129
- </ul>
130
- )}
131
-
132
- {choices === null ? (
133
- <div class="row h-3 al-center">
134
- <i src="spin" />
135
- </div>
136
- ) : (
137
- <ul class="col menu">
138
- {choices.map( choice => {
139
-
140
- const isCurrent = currentList.some(c => c.value === choice.value);
141
-
142
- return !isCurrent && (
143
- <li>
144
- <Button size="s" onClick={() => {
145
- onChange( current => {
146
- return multiple
147
- ? [...(current || []), choice]
148
- : choice
149
- });
150
- }}>
151
- {/*search.keywords ? (
152
- <span>
153
-
154
- <strong>{search.keywords}</strong>{choice.label.slice( search.keywords.length )}
155
-
156
- </span>
157
- ) : */choice.label}
158
- </Button>
159
- </li>
160
- )
161
- })}
162
-
163
- {((!required || !validator?.options.min) && noneSelection) && (
164
- <li>
165
- <Button size="s" onClick={() => onChange(multiple ? [] : undefined)}
166
- suffix={(current === undefined || (multiple && current.length === 0)) && <i src="check" class="fg primary" />}>
167
- {noneSelection}
168
- </Button>
169
- </li>
170
- )}
171
- </ul>
172
- )}
173
- </div>
174
- )
175
-
176
55
  return <>
177
- {inline ? selector : (
178
- <Dropdown {...props} content={selector} iconR="chevron-down" refModal={refModal}>
56
+ {props.inline ? (
57
+ <ChoiceSelector {...props} currentList={currentList} />
58
+ ) : (
59
+ <Dropdown {...props} content={(() =>
60
+ <ChoiceSelector {...props} currentList={currentList} />
61
+ )} iconR="chevron-down" refModal={refModal}>
179
62
 
180
63
  {currentList.length === 0
181
64
  ? title
182
- : currentList.map(choice => choice.label).join(', ')}
65
+ : currentList.map(
66
+ choice => (
67
+ <span class="badge">
68
+ {choice.label}
69
+ </span>
70
+ )
71
+ )}
183
72
 
184
73
  </Dropdown>
185
74
  )}
@@ -2,6 +2,8 @@ export type TSide = "left"|"top"|"right"|"bottom";
2
2
 
3
3
  const debug = true;
4
4
 
5
+ export type TPosition = ReturnType<typeof corrigerPosition>
6
+
5
7
  export default function corrigerPosition(
6
8
  conteneur: HTMLElement,
7
9
  popover: HTMLElement,
@@ -20,10 +22,10 @@ export default function corrigerPosition(
20
22
  height: popover.offsetHeight
21
23
  }
22
24
 
23
- debug && console.log(`[positionnement] Conteneur =`, conteneur, `Popover =`, popover, `Coté =`, cote);
25
+ debug && console.log(`[popover] Conteneur =`, conteneur, `Popover =`, popover, `Coté =`, cote);
24
26
 
25
27
  if (dimsPop.width === undefined || dimsPop.height === undefined)
26
- console.error(`Impossible de récupérer les dimensions de la popover. popover est-il réelement n élement html, ou alors un composant react ? | dimsPop =`, dimsPop, 'popover =', popover);
28
+ console.error("Unable to get the dimensions of the popover element. Did you pass a react element as content of popover ?");
27
29
 
28
30
  /*----------------------------------
29
31
  - POSITIONNEMENT INITIAL
@@ -37,6 +39,7 @@ export default function corrigerPosition(
37
39
  const posCont = conteneur.getBoundingClientRect();
38
40
 
39
41
  // Placement
42
+ debug && console.log(`[popover] Placement = ${cote}`, posCont.height, margeY);
40
43
  switch (cote) {
41
44
  case 'top':
42
45
  posInit.top = -dimsPop.height - margeY;
@@ -54,12 +57,12 @@ export default function corrigerPosition(
54
57
 
55
58
  // Centrage Horizontal
56
59
  if (cote === 'top' || cote === 'bottom') {
57
- debug && console.log(`[positionnement] Centrage horizontal`);
60
+ debug && console.log(`[popover] Centrage horizontal`);
58
61
  posInit.left = posCont.width / 2 - dimsPop.width / 2;
59
62
  }
60
63
  // Centrage Vertical
61
64
  if (cote === 'left' || cote === 'right') {
62
- debug && console.log(`[positionnement] Centrage vertical`);
65
+ debug && console.log(`[popover] Centrage vertical`);
63
66
  posInit.top = posCont.height / 2 - dimsPop.height / 2;
64
67
  }
65
68
 
@@ -96,34 +99,34 @@ export default function corrigerPosition(
96
99
  bottom: 'auto'
97
100
  };
98
101
 
99
- debug && console.log(`[positionnement] Position initiale =`, posInit, `Frontières =`, frame, '=', frontieres);
102
+ debug && console.log(`[popover] Position initiale =`, posInit, `Frontières =`, frame, '=', frontieres);
100
103
 
101
104
  // Extrémité Haut
102
105
  if (posCont.top + posInit.top < frontieres.top) {
103
106
  posFinale.top = (frontieres.top - posCont.top) + 'px';
104
- debug && console.log(`[positionnement] Top: Extremité haut`, posFinale.top);
107
+ debug && console.log(`[popover] Top: Extremité haut`, posFinale.top);
105
108
  // Extrémité Bas
106
109
  } else if (posCont.top + posInit.top + dimsPop.height >= frontieres.bottom) {
107
110
  posFinale.top = 'auto';
108
111
  posFinale.bottom = (posCont.bottom - frontieres.bottom) + 'px';
109
- debug && console.log(`[positionnement] Top: Extremité bas`, posFinale.bottom);
112
+ debug && console.log(`[popover] Top: Extremité bas`, posFinale.bottom);
110
113
  } else {
111
114
  posFinale.top = posInit.top + 'px';
112
- debug && console.log(`[positionnement] Top: Conservation`, posFinale.top);
115
+ debug && console.log(`[popover] Top: Conservation`, posFinale.top);
113
116
  }
114
117
 
115
118
  // Extrémité Gauche
116
119
  if (posCont.left + posInit.left < frontieres.left) {
117
120
  posFinale.left = (frontieres.left - posCont.left) + 'px';
118
- debug && console.log(`[positionnement] Left: Extremité gauche`, posFinale.left);
121
+ debug && console.log(`[popover] Left: Extremité gauche`, posFinale.left);
119
122
  // Extrémité Droite
120
123
  } else if (posCont.left + posInit.left + dimsPop.width >= frontieres.right) {
121
124
  posFinale.left = 'auto';
122
125
  posFinale.right = (posCont.right - frontieres.right) + 'px';
123
- debug && console.log(`[positionnement] Left: Extremité droite`, posFinale.right);
126
+ debug && console.log(`[popover] Left: Extremité droite`, posFinale.right);
124
127
  } else {
125
128
  posFinale.left = posInit.left + 'px';
126
- debug && console.log(`[positionnement] Left: Conservation`, posFinale.left);
129
+ debug && console.log(`[popover] Left: Conservation`, posFinale.left);
127
130
  }
128
131
 
129
132
  console.log({ posInit, dimsPop, frontieres }, { posFinale });
@@ -3,19 +3,12 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  // Npm
6
- import React, { JSX } from 'react';
7
- import { ComponentChild } from 'preact';
6
+ import React, { JSX, ComponentChild } from 'react';
8
7
  import type { StateUpdater } from 'preact/hooks';
9
8
 
10
- // Composants
11
- import Bouton, { Props as PropsBouton } from '@client/components/button';
12
-
13
- // Ressouces
14
- //import '@client/components/Donnees/Tooltip/index.less';
15
-
16
9
  // Libs
17
- import getPosition, { TSide } from './getPosition';
18
- import { blurable, deepContains } from '@client/utils/dom';
10
+ import getPosition, { TSide, TPosition } from './getPosition';
11
+ import { blurable, deepContains, focusContent } from '@client/utils/dom';
19
12
  import useContexte from '@/client/context';
20
13
 
21
14
  /*----------------------------------
@@ -26,7 +19,7 @@ export type Props = {
26
19
  id?: string,
27
20
 
28
21
  // Display
29
- content?: JSX.Element,
22
+ content?: JSX.Element | (() => JSX.Element),
30
23
  state: [boolean, StateUpdater<boolean>],
31
24
  width?: number | string,
32
25
  disable?: boolean
@@ -44,8 +37,10 @@ export type Props = {
44
37
  import "./popover.less";
45
38
  export default (props: Props) => {
46
39
 
47
- const ctx = useContexte();
48
-
40
+ /*----------------------------------
41
+ - INIT
42
+ ----------------------------------*/
43
+
49
44
  let {
50
45
  id,
51
46
 
@@ -58,31 +53,39 @@ export default (props: Props) => {
58
53
  ...autresProps
59
54
  } = props;
60
55
 
61
- const [position, setPosition] = React.useState(undefined);
56
+ const [position, setPosition] = React.useState<TPosition>(undefined);
62
57
  const refCont = React.useRef<HTMLElement>(null);
63
- const refPop = React.useRef<HTMLElement>(null);
58
+ const refContent = React.useRef<HTMLElement>(null);
64
59
 
65
60
  const [shown, show] = state;
66
61
 
67
62
  // Màj visibilite
68
63
  React.useEffect(() => {
69
64
  if (shown === true) {
65
+
70
66
  // Positionnement si affichage
71
67
  setPosition(
72
68
  getPosition(
73
69
  refCont.current,
74
- refPop.current,
70
+ refContent.current,
75
71
  false,
76
- position,
72
+ side,
77
73
  frame || document.getElementById('page')
78
74
  )
79
75
  );
80
76
 
81
- //return blurable([refCont, () => show(false)])
77
+ // Autofocus elements
78
+ focusContent( refContent.current );
79
+
80
+ // Close when the user clicks elsewere tha the popover
81
+ return blurable([ refCont.current, () => show(false) ])
82
82
  }
83
83
 
84
84
  }, [shown]);
85
85
 
86
+ /*----------------------------------
87
+ - ATTRIBUTES
88
+ ----------------------------------*/
86
89
  if (!autresProps.className)
87
90
  autresProps.className = '';
88
91
 
@@ -95,6 +98,36 @@ export default (props: Props) => {
95
98
 
96
99
  const Tag = tag || 'div';
97
100
 
101
+ let renderedContent: ComponentChild;
102
+ if (active) {
103
+ content = typeof content === 'function' ? React.createElement(content) : content;
104
+ renderedContent = React.cloneElement(
105
+ content,
106
+ {
107
+ className: (content.props.className || '')
108
+ + ' card white popover pd-1'
109
+ + (position ? ' pos_' + position.cote : ''),
110
+ ref: (ref: any) => {
111
+ if (ref !== null)
112
+ refContent.current = ref
113
+ },
114
+ style: {
115
+ ...(content.props.style || {}),
116
+ ...(position ? {
117
+ top: position.css.top,
118
+ left: position.css.left,
119
+ right: position.css.right,
120
+ bottom: position.css.bottom,
121
+ } : {}),
122
+ ...(width !== undefined ? { width: typeof width === 'number' ? width + 'rem' : width } : {})
123
+ }
124
+ }
125
+ )
126
+ }
127
+
128
+ /*----------------------------------
129
+ - RENDER
130
+ ----------------------------------*/
98
131
  return (
99
132
  <Tag
100
133
  style={{
@@ -115,23 +148,7 @@ export default (props: Props) => {
115
148
  }
116
149
  })}
117
150
 
118
- {active && React.cloneElement(content, {
119
- className: (content.props.className || '') + ' card white popover' + (position ? ' pos_' + position.cote : ''),
120
- ref: (ref: any) => {
121
- if (ref !== null)
122
- refPop.current = ref
123
- },
124
- style: {
125
- ...(content.props.style || {}),
126
- ...(position ? {
127
- top: position.css.top,
128
- left: position.css.left,
129
- right: position.css.right,
130
- bottom: position.css.bottom,
131
- } : {}),
132
- ...(width !== undefined ? { width: typeof width === 'number' ? width + 'rem' : width } : {})
133
- }
134
- })}
151
+ {renderedContent}
135
152
  </Tag>
136
153
  )
137
154
  }
@@ -3,8 +3,14 @@
3
3
  .contPopover {
4
4
  display: inline-block;
5
5
 
6
- > button {
6
+ > .btn {
7
7
  width: 100%;
8
+
9
+ > .label {
10
+ flex: 1;
11
+ justify-content: flex-start;
12
+ overflow: hidden;
13
+ }
8
14
  }
9
15
 
10
16
  // Evite les dépassements à droite en en bas de la page
@@ -18,7 +18,7 @@ import Popover from '../containers/Popover';
18
18
  export type { TDialogControls } from '../Dialog/Manager';
19
19
 
20
20
  export type Props = ButtonProps & {
21
- content: ComponentChild,
21
+ content: ComponentChild | (() => ComponentChild),
22
22
  refModal?: RefObject<TDialogControls>
23
23
  }
24
24
 
@@ -26,7 +26,7 @@ export { default as CircularProgressbar } from './data/progressbar/circular';
26
26
 
27
27
  // Input V3
28
28
  export { default as Select } from './Select';
29
- export { default as String } from './inputv3/string';
29
+ export { default as Input } from './inputv3';
30
30
  export { default as File } from './inputv3/file';
31
31
 
32
32
  // TOD: fix popover component
@@ -58,13 +58,13 @@ export default Champ<Props, TValeurDefaut, TValeurOut>('number', { valeurDefaut,
58
58
  }
59
59
 
60
60
  const boutonsControle = [{
61
- icone: /* @icone */"minus",
61
+ icon: /* @icon */"minus",
62
62
  onClick: () => {
63
63
  const pasA = pas >= valeur ? pas / 10 : pas;
64
64
  setState({ valeur: corrigerValeur(valeur - pasA) }, true);
65
65
  }
66
66
  }, {
67
- icone: /* @icone */"plus",
67
+ icon: /* @icon */"plus",
68
68
  onClick: () => {
69
69
  setState({ valeur: corrigerValeur(valeur + pas) }, true);
70
70
  }
@@ -8,7 +8,7 @@ import { ComponentChild, JSX } from 'preact';
8
8
  import TextareaAutosize from 'react-textarea-autosize';
9
9
 
10
10
  // Core libs
11
- import { useInput, InputBaseProps } from '../base';
11
+ import { useInput, InputBaseProps } from './base';
12
12
 
13
13
  /*----------------------------------
14
14
  - TYPES
@@ -25,7 +25,7 @@ export type Props = {
25
25
  inputRef?: React.Ref<HTMLInputElement>
26
26
 
27
27
  // Behavior
28
- type?: 'email' | 'password' | 'longtext',
28
+ type?: 'email' | 'password' | 'longtext' | 'number',
29
29
  choice?: string[] | ((input: string) => Promise<string[]>),
30
30
 
31
31
  // Actions
@@ -93,6 +93,10 @@ export default ({
93
93
  prefix = prefix || <i src="text" />;
94
94
  Tag = 'textarea'//TextareaAutosize;
95
95
 
96
+ } else if (type === 'number') {
97
+
98
+ fieldProps.type = 'number';
99
+
96
100
  }
97
101
 
98
102
  // Auto suffix
@@ -23,7 +23,7 @@ router.error( 403, ({ message, modal }) => {
23
23
  });
24
24
 
25
25
  return (
26
- <div class="col pd-2">
26
+ <div class="card w-3-4 col al-center pd-2">
27
27
 
28
28
  <i src="times-circle" class="fg error xxl" />
29
29
 
@@ -23,7 +23,7 @@ router.error( 500, ({ message }) => {
23
23
  });
24
24
 
25
25
  return (
26
- <div class="col pd-2">
26
+ <div class="card w-3-4 col al-center pd-2">
27
27
 
28
28
  <i src="times-circle" class="fg error xxl" />
29
29
 
@@ -5,7 +5,7 @@
5
5
  import React from 'react';
6
6
 
7
7
  // Core components
8
- import { Button, String } from '@client/components';
8
+ import { Button, Input } from '@client/components';
9
9
  import Card, { Props as CardProps } from '@client/components/Dialog/card';
10
10
 
11
11
  // Core libs
@@ -49,11 +49,11 @@ export default ({ ...self }: {} & CardProps) => {
49
49
 
50
50
  <p>What's the problem ?</p>
51
51
 
52
- <String type="longtext" title="Description of the problem" value={observation} onChange={setObservation} />
52
+ <Input type="longtext" title="Description of the problem" value={observation} onChange={setObservation} />
53
53
 
54
54
  <p>What did you do just before the problem occurs ?</p>
55
55
 
56
- <String type="longtext" title="How the problem occured ?" value={before} onChange={setBefore} />
56
+ <Input type="longtext" title="How the problem occured ?" value={before} onChange={setBefore} />
57
57
 
58
58
  </Card>
59
59
  )
@@ -47,7 +47,7 @@ export const blurable = (...args: [HTMLElement, Function][]) => {
47
47
 
48
48
  if (!deepContains([refElement], e.target))
49
49
  masquer();
50
-
50
+
51
51
  }
52
52
  }
53
53
 
@@ -65,4 +65,15 @@ export const blurable = (...args: [HTMLElement, Function][]) => {
65
65
  window.removeEventListener('mousedown', blur);
66
66
  unlisten();
67
67
  }
68
+ }
69
+
70
+ export const focusContent = ( container: HTMLElement ) => {
71
+
72
+ const toFocus = container.querySelector(
73
+ 'input, textarea, button.btn.primary, footer > button.btn'
74
+ ) || container;
75
+
76
+ console.log('Element to focus', toFocus);
77
+ // TODO: Type only docusable elemnts
78
+ toFocus?.focus();
68
79
  }