@ouestfrance/sipa-bms-ui 8.19.0 → 8.21.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 (66) hide show
  1. package/dist/components/form/BmsFilePicker.vue.d.ts +4 -0
  2. package/dist/components/form/BmsInputText.vue.d.ts +1 -0
  3. package/dist/components/form/BmsMultiSelect.vue.d.ts +4 -0
  4. package/dist/components/form/BmsSearch.vue.d.ts +3 -1
  5. package/dist/components/form/RawAutocomplete.vue.d.ts +8 -0
  6. package/dist/components/layout/BmsSplitWindow.vue.d.ts +1 -0
  7. package/dist/components/navigation/UiTenantSwitcher.vue.d.ts +3 -1
  8. package/dist/components/table/BmsTableFilters.vue.d.ts +3 -1
  9. package/dist/mockServiceWorker.js +1 -1
  10. package/dist/sipa-bms-ui.css +54 -368
  11. package/dist/sipa-bms-ui.es.js +118 -47
  12. package/dist/sipa-bms-ui.es.js.map +1 -1
  13. package/dist/sipa-bms-ui.umd.js +117 -46
  14. package/dist/sipa-bms-ui.umd.js.map +1 -1
  15. package/package.json +13 -12
  16. package/src/assets/scss/_conf.scss +0 -1
  17. package/src/assets/scss/app.scss +0 -1
  18. package/src/components/button/BmsAllButtons.stories.js +50 -23
  19. package/src/components/button/BmsButton.stories.js +151 -65
  20. package/src/components/button/BmsIconButton.stories.js +14 -8
  21. package/src/components/button/UiButton.stories.js +31 -0
  22. package/src/components/feedback/BmsCircularProgress.stories.js +0 -7
  23. package/src/components/feedback/BmsLoader.stories.js +0 -6
  24. package/src/components/feedback/BmsTooltip.stories.js +1 -0
  25. package/src/components/feedback/UiTooltip.stories.js +1 -0
  26. package/src/components/form/BmsAutocomplete.stories.js +11 -1
  27. package/src/components/form/BmsBetweenInput.stories.js +17 -1
  28. package/src/components/form/BmsFilePicker.stories.js +8 -1
  29. package/src/components/form/BmsFilePicker.vue +10 -5
  30. package/src/components/form/BmsInputBooleanCheckbox.stories.js +9 -0
  31. package/src/components/form/BmsInputCheckboxCaption.stories.js +16 -0
  32. package/src/components/form/BmsInputCheckboxCaptionGroup.stories.js +21 -1
  33. package/src/components/form/BmsInputText.vue +1 -0
  34. package/src/components/form/BmsMultiSelect.vue +32 -25
  35. package/src/components/form/BmsSelect.vue +18 -16
  36. package/src/components/form/RawAutocomplete.vue +16 -4
  37. package/src/components/form/RawInputText.vue +1 -0
  38. package/src/components/form/UiBmsInputCheckbox.stories.js +1 -0
  39. package/src/components/form/UiBmsSwitch.stories.js +1 -5
  40. package/src/components/layout/BmsForm_retrocompat.stories.js +1 -0
  41. package/src/components/layout/BmsModal.stories.js +2 -1
  42. package/src/components/layout/BmsSplitWindow.vue +4 -3
  43. package/src/components/navigation/BmsBreadcrumb.stories.js +0 -18
  44. package/src/components/navigation/BmsMenu.stories.js +4 -4
  45. package/src/components/navigation/BmsMenuNav.stories.js +4 -3
  46. package/src/components/navigation/UiMenuItem.stories.js +53 -2
  47. package/src/components/navigation/UiTab.stories.js +1 -0
  48. package/src/components/navigation/UiTenantSwitcher.stories.js +1 -0
  49. package/src/components/table/BmsEmptyScreen.stories.js +0 -7
  50. package/src/components/table/BmsTableFilters.vue +1 -1
  51. package/src/components/table/UiBmsTable.stories.js +1 -0
  52. package/src/components/table/UiFilterButton.stories.js +3 -8
  53. package/src/components/utils/BmsRelativeTime.stories.js +0 -6
  54. package/src/documentation/button/primaryButton.mdx +142 -0
  55. package/src/documentation/{secondaryButton.mdx → button/secondaryButton.mdx} +2 -2
  56. package/src/documentation/foundation/contributing.mdx +72 -0
  57. package/src/documentation/foundation/gettingstarted.mdx +7 -0
  58. package/src/documentation/{principles.mdx → foundation/principles.mdx} +9 -9
  59. package/src/documentation/icons.mdx +43 -0
  60. package/src/showroom/pages/forms.vue +10 -1
  61. package/src/assets/scss/_formkit.scss +0 -353
  62. package/src/components/feedback/Notification.stories.js +0 -37
  63. package/src/components/form/Form.stories.js +0 -35
  64. package/src/components/navigation/UiMenuItemStatus.stories.js +0 -64
  65. package/src/documentation/primaryButton.mdx +0 -20
  66. /package/src/documentation/{button.mdx → button/button.mdx} +0 -0
@@ -2,6 +2,7 @@ import BmsAutocomplete from '@/components/form/BmsAutocomplete.vue';
2
2
  import { Cat, Heart, Wheat } from 'lucide-vue-next';
3
3
 
4
4
  import template from '@/documentation/template_field_dependency.mdx';
5
+ import { DEFAULT_FIELD_INPUT } from '../../../.storybook/constants';
5
6
 
6
7
  export default {
7
8
  parameters: {
@@ -9,7 +10,8 @@ export default {
9
10
  page: template,
10
11
  },
11
12
  },
12
- title: 'Composants/form/AutocompleteTextIcon',
13
+ tags: ['with_useable_code'],
14
+ title: 'Composants/form/Autocomplete',
13
15
  component: BmsAutocomplete,
14
16
  argTypes: {},
15
17
  };
@@ -31,6 +33,7 @@ const Template = (args) => ({
31
33
 
32
34
  export const Default = Template.bind({});
33
35
  Default.args = {
36
+ ...DEFAULT_FIELD_INPUT,
34
37
  label: 'My autocomplete field',
35
38
  options: [
36
39
  { label: 'titi', value: 'i' },
@@ -42,6 +45,7 @@ Default.args = {
42
45
 
43
46
  export const Disabled = Template.bind({});
44
47
  Disabled.args = {
48
+ ...DEFAULT_FIELD_INPUT,
45
49
  label: 'My autocomplete field',
46
50
  options: [
47
51
  { label: 'titi', value: 'i' },
@@ -54,6 +58,7 @@ Disabled.args = {
54
58
 
55
59
  export const Loading = Template.bind({});
56
60
  Loading.args = {
61
+ ...DEFAULT_FIELD_INPUT,
57
62
  label: 'My autocomplete field',
58
63
  loading: true,
59
64
  options: [
@@ -66,6 +71,7 @@ Loading.args = {
66
71
 
67
72
  export const WithStringArray = Template.bind({});
68
73
  WithStringArray.args = {
74
+ ...DEFAULT_FIELD_INPUT,
69
75
  label: 'My autocomplete field',
70
76
  options: ['titi', 'toto', 'tutu'],
71
77
  modelValue: '',
@@ -74,6 +80,7 @@ WithStringArray.args = {
74
80
 
75
81
  export const WithValue = Template.bind({});
76
82
  WithValue.args = {
83
+ ...DEFAULT_FIELD_INPUT,
77
84
  label: 'My autocomplete field',
78
85
  options: [
79
86
  { label: 'titi', value: 'i' },
@@ -86,6 +93,7 @@ WithValue.args = {
86
93
  export const Opened = Template.bind({});
87
94
  Opened.parameters = { pseudo: { focus: 'input' } };
88
95
  Opened.args = {
96
+ ...DEFAULT_FIELD_INPUT,
89
97
  label: 'My autocomplete field',
90
98
  options: [
91
99
  { label: 'titi', value: 'i' },
@@ -97,6 +105,7 @@ Opened.args = {
97
105
  };
98
106
  export const IconsWithValue = Template.bind({});
99
107
  IconsWithValue.args = {
108
+ ...DEFAULT_FIELD_INPUT,
100
109
  label: 'My autocomplete field',
101
110
  options: [
102
111
  { label: 'titi', value: 'i', icon: Heart },
@@ -109,6 +118,7 @@ IconsWithValue.args = {
109
118
  export const IconsOpened = Template.bind({});
110
119
  IconsOpened.parameters = { pseudo: { focus: 'input' } };
111
120
  IconsOpened.args = {
121
+ ...DEFAULT_FIELD_INPUT,
112
122
  label: 'My autocomplete field',
113
123
  options: [
114
124
  { label: 'titi', value: 'i', icon: Heart },
@@ -1,6 +1,6 @@
1
1
  import { BookOpen, CloudLightning } from 'lucide-vue-next';
2
2
  import BmsBetweenInput from './BmsBetweenInput.vue';
3
- import { StatusType, InputType } from '../../models';
3
+ import { InputType, StatusType } from '../../models';
4
4
 
5
5
  import template from '@/documentation/template_field_dependency.mdx';
6
6
 
@@ -11,6 +11,7 @@ export default {
11
11
  },
12
12
  },
13
13
  title: 'Composants/form/BetweenInput',
14
+ tags: ['with_useable_code'],
14
15
  component: BmsBetweenInput,
15
16
  };
16
17
 
@@ -32,8 +33,20 @@ const Template = (args) => ({
32
33
  `,
33
34
  });
34
35
 
36
+ const DEFAULT_BEETWEEN_INPUT = {
37
+ inputType: InputType.NUMBER,
38
+ label: 'My label',
39
+ required: false,
40
+ optional: false,
41
+ disabled: false,
42
+ helperText: 'help me !',
43
+ captions: [],
44
+ errors: [],
45
+ };
46
+
35
47
  export const Default = Template.bind({});
36
48
  Default.args = {
49
+ ...DEFAULT_BEETWEEN_INPUT,
37
50
  label: 'My label',
38
51
  placeholderFrom: 'Placeholder From',
39
52
  placeholderTo: 'Placeholder To',
@@ -42,6 +55,7 @@ Default.args = {
42
55
 
43
56
  export const WithDate = Template.bind({});
44
57
  WithDate.args = {
58
+ ...DEFAULT_BEETWEEN_INPUT,
45
59
  inputType: InputType.DATETIME,
46
60
  label: 'My label',
47
61
  placeholderFrom: 'Placeholder From',
@@ -51,6 +65,7 @@ WithDate.args = {
51
65
 
52
66
  export const WithBadPresetValueDate = Template.bind({});
53
67
  WithBadPresetValueDate.args = {
68
+ ...DEFAULT_BEETWEEN_INPUT,
54
69
  inputType: InputType.DATETIME,
55
70
  label: 'My label',
56
71
  placeholderFrom: 'Placeholder From',
@@ -62,6 +77,7 @@ WithBadPresetValueDate.args = {
62
77
 
63
78
  export const WithErrors = Template.bind({});
64
79
  WithErrors.args = {
80
+ ...DEFAULT_BEETWEEN_INPUT,
65
81
  label: 'My label',
66
82
  placeholderFrom: 'Placeholder From',
67
83
  placeholderTo: 'Placeholder To',
@@ -9,6 +9,7 @@ export default {
9
9
  },
10
10
  },
11
11
  title: 'Composants/form/FilePicker',
12
+ tags: ['with_useable_code'],
12
13
  component: BmsFilePicker,
13
14
  argTypes: {
14
15
  limit: {
@@ -31,10 +32,16 @@ const Template = (args) => ({
31
32
  });
32
33
 
33
34
  export const Empty = Template.bind({});
34
- Empty.args = {};
35
+ Empty.args = {
36
+ modelValue: [],
37
+ limit: 5,
38
+ dragOverMessage: 'Déposer vos fichiers ici',
39
+ dragOffMessage: 'Glissez votre fichier ici ou cliquez pour parcourir',
40
+ };
35
41
 
36
42
  export const WithFiles = Template.bind({});
37
43
  WithFiles.args = {
44
+ limit: 5,
38
45
  modelValue: [
39
46
  {
40
47
  name: 'Name of a file',
@@ -7,9 +7,16 @@ const files: Ref<File[] | undefined> = ref();
7
7
  const isDragOver = ref(false);
8
8
 
9
9
  const props = withDefaults(
10
- defineProps<{ modelValue?: File[]; limit: number }>(),
10
+ defineProps<{
11
+ dragOverMessage?: string;
12
+ dragOffMessage?: string;
13
+ modelValue?: File[];
14
+ limit: number;
15
+ }>(),
11
16
  {
12
17
  limit: 10,
18
+ dragOverMessage: 'Déposer votre image ici',
19
+ dragOffMessage: 'Glissez votre image ici ou cliquez pour parcourir',
13
20
  },
14
21
  );
15
22
 
@@ -101,10 +108,8 @@ function onDeleteFile(file: File) {
101
108
  @dragover.prevent
102
109
  >
103
110
  <label class="file-upload__label">
104
- <template v-if="isDragOver">Déposer votre image ici</template>
105
- <template v-else
106
- >Glissez votre image ici ou cliquez pour parcourir</template
107
- >
111
+ <template v-if="isDragOver">{{ dragOverMessage }}</template>
112
+ <template v-else>{{ dragOffMessage }}</template>
108
113
  <input
109
114
  data-testid="file-upload-input-file"
110
115
  type="file"
@@ -3,6 +3,7 @@ import { ref } from 'vue';
3
3
  import { StatusType } from '@/models';
4
4
 
5
5
  import template from '@/documentation/template_field_dependency.mdx';
6
+ import { DEFAULT_FIELD_INPUT } from '../../../.storybook/constants';
6
7
 
7
8
  export default {
8
9
  parameters: {
@@ -10,6 +11,7 @@ export default {
10
11
  page: template,
11
12
  },
12
13
  },
14
+ tags: ['with_useable_code'],
13
15
  title: 'Composants/form/InputBooleanCheckbox',
14
16
  component: BmsInputBooleanCheckbox,
15
17
  };
@@ -42,12 +44,14 @@ const Template = (args) => ({
42
44
 
43
45
  export const Default = Template.bind({});
44
46
  Default.args = {
47
+ ...DEFAULT_FIELD_INPUT,
45
48
  labelValue: 'check me !',
46
49
  name: 'myCheckboxName',
47
50
  };
48
51
 
49
52
  export const WithNotBooleanModelValue = Template.bind({});
50
53
  WithNotBooleanModelValue.args = {
54
+ ...DEFAULT_FIELD_INPUT,
51
55
  modelValue: 'COCHEEEEEEEE',
52
56
  labelValue: 'check me !',
53
57
  name: 'myCheckboxName',
@@ -55,6 +59,7 @@ WithNotBooleanModelValue.args = {
55
59
 
56
60
  export const DefaultWithCheckedValue = Template.bind({});
57
61
  DefaultWithCheckedValue.args = {
62
+ ...DEFAULT_FIELD_INPUT,
58
63
  modelValue: true,
59
64
  labelValue: 'check me !',
60
65
  name: 'myCheckboxName',
@@ -62,6 +67,7 @@ DefaultWithCheckedValue.args = {
62
67
 
63
68
  export const WithLabel = Template.bind({});
64
69
  WithLabel.args = {
70
+ ...DEFAULT_FIELD_INPUT,
65
71
  label: 'My label',
66
72
  labelValue: 'check me !',
67
73
  modelValue: false,
@@ -70,6 +76,7 @@ WithLabel.args = {
70
76
 
71
77
  export const WithLabelAsSlot = Template.bind({});
72
78
  WithLabelAsSlot.args = {
79
+ ...DEFAULT_FIELD_INPUT,
73
80
  name: 'myCheckboxName',
74
81
  labelValue: 'check me !',
75
82
  modelValue: false,
@@ -78,6 +85,7 @@ WithLabelAsSlot.args = {
78
85
 
79
86
  export const Disabled = Template.bind({});
80
87
  Disabled.args = {
88
+ ...DEFAULT_FIELD_INPUT,
81
89
  name: 'myCheckboxName',
82
90
  labelValue: 'check me !',
83
91
  label: 'My label',
@@ -87,6 +95,7 @@ Disabled.args = {
87
95
 
88
96
  export const Error = Template.bind({});
89
97
  Error.args = {
98
+ ...DEFAULT_FIELD_INPUT,
90
99
  name: 'myCheckboxName',
91
100
  labelValue: 'check me !',
92
101
  label: 'My label',
@@ -9,6 +9,7 @@ export default {
9
9
  page: template,
10
10
  },
11
11
  },
12
+ tags: ['with_useable_code'],
12
13
  title: 'Composants/form/InputCheckboxCaption',
13
14
  component: BmsInputCheckboxCaption,
14
15
  };
@@ -38,8 +39,18 @@ const Template = (args) => ({
38
39
  `,
39
40
  });
40
41
 
42
+ const DEFAULT_CHECKBOXCAPTION_INPUT = {
43
+ label: 'Mon texte',
44
+ options: [],
45
+ required: false,
46
+ disabled: false,
47
+ captions: [],
48
+ errors: [],
49
+ };
50
+
41
51
  export const Default = Template.bind({});
42
52
  Default.args = {
53
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
43
54
  modelValue: null,
44
55
  label: 'MyLabel',
45
56
  name: 'myInput',
@@ -47,6 +58,7 @@ Default.args = {
47
58
 
48
59
  export const WithLabelAsSlot = Template.bind({});
49
60
  WithLabelAsSlot.args = {
61
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
50
62
  modelValue: null,
51
63
  labelSlot: 'This is a slotted value',
52
64
  name: 'myInput',
@@ -54,6 +66,7 @@ WithLabelAsSlot.args = {
54
66
 
55
67
  export const WithCaptions = Template.bind({});
56
68
  WithCaptions.args = {
69
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
57
70
  label: 'My label',
58
71
  modelValue: null,
59
72
  captions: ['Caption form my label 1', 'Caption form my label 2'],
@@ -62,6 +75,7 @@ WithCaptions.args = {
62
75
 
63
76
  export const WithCaptionMode = Template.bind({});
64
77
  WithCaptionMode.args = {
78
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
65
79
  label: 'My label',
66
80
  modelValue: null,
67
81
  captions: [
@@ -75,6 +89,7 @@ WithCaptionMode.args = {
75
89
 
76
90
  export const Disabled = Template.bind({});
77
91
  Disabled.args = {
92
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
78
93
  label: 'My label',
79
94
  modelValue: null,
80
95
  disabled: true,
@@ -84,6 +99,7 @@ Disabled.args = {
84
99
 
85
100
  export const Error = Template.bind({});
86
101
  Error.args = {
102
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
87
103
  label: 'My label',
88
104
  modelValue: null,
89
105
  errors: ['Error 1', 'Error 2'],
@@ -11,6 +11,7 @@ export default {
11
11
  page: template,
12
12
  },
13
13
  },
14
+ tags: ['with_useable_code'],
14
15
  title: 'Composants/form/InputCheckboxCaptionGroup',
15
16
  component: BmsInputCheckboxCaptionGroup,
16
17
  };
@@ -30,8 +31,21 @@ const Template = (args) => ({
30
31
  `,
31
32
  });
32
33
 
34
+ const DEFAULT_CHECKBOXCAPTION_INPUT = {
35
+ options: [],
36
+ required: false,
37
+ optional: false,
38
+ disabled: false,
39
+ helperText: 'help me !',
40
+ captions: [],
41
+ errors: [],
42
+ align: 'right',
43
+ };
44
+
33
45
  export const DefaultRow = Template.bind({});
34
46
  DefaultRow.args = {
47
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
48
+ label: 'This is a field label',
35
49
  options: [
36
50
  { value: 'tata', label: 'Tata', captions: ['Caption for tata'] },
37
51
  { value: 'titi', label: 'Titi', captions: ['Caption for titi'] },
@@ -43,11 +57,11 @@ DefaultRow.args = {
43
57
  { label: 'Caption for group', mode: StatusType.Success },
44
58
  ],
45
59
  modelValue: [],
46
- label: 'This is a field label',
47
60
  };
48
61
 
49
62
  export const DefaultColumn = Template.bind({});
50
63
  DefaultColumn.args = {
64
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
51
65
  options: [
52
66
  { value: 'tata', captions: ['Caption for tata'] },
53
67
  { value: 'titi', captions: ['Caption for titi'] },
@@ -61,6 +75,7 @@ DefaultColumn.args = {
61
75
 
62
76
  export const WithCaptionMode = Template.bind({});
63
77
  WithCaptionMode.args = {
78
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
64
79
  options: [
65
80
  {
66
81
  value: 'tata',
@@ -89,6 +104,7 @@ WithCaptionMode.args = {
89
104
 
90
105
  export const DefaultSelected = Template.bind({});
91
106
  DefaultSelected.args = {
107
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
92
108
  options: [
93
109
  { value: 'tata', captions: ['Caption for tata'] },
94
110
  { value: 'titi', captions: ['Caption for titi'] },
@@ -101,6 +117,7 @@ DefaultSelected.args = {
101
117
 
102
118
  export const FullHelp = Template.bind({});
103
119
  FullHelp.args = {
120
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
104
121
  options: [
105
122
  { value: 'tata', captions: ['Caption for tata'] },
106
123
  { value: 'titi', captions: ['Caption for titi'] },
@@ -115,6 +132,7 @@ FullHelp.args = {
115
132
 
116
133
  export const Disabled = Template.bind({});
117
134
  Disabled.args = {
135
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
118
136
  options: [
119
137
  { value: 'tata', captions: ['Caption for tata'] },
120
138
  { value: 'titi', captions: ['Caption for titi'] },
@@ -128,6 +146,7 @@ Disabled.args = {
128
146
 
129
147
  export const Required = Template.bind({});
130
148
  Required.args = {
149
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
131
150
  options: [
132
151
  { value: 'tata', captions: ['Caption for tata'] },
133
152
  { value: 'titi', captions: ['Caption for titi'] },
@@ -141,6 +160,7 @@ Required.args = {
141
160
 
142
161
  export const Errors = Template.bind({});
143
162
  Errors.args = {
163
+ ...DEFAULT_CHECKBOXCAPTION_INPUT,
144
164
  options: [
145
165
  { value: 'tata', captions: ['Caption for tata'] },
146
166
  { value: 'titi', captions: ['Caption for titi'] },
@@ -53,6 +53,7 @@ const props = withDefaults(defineProps<Props>(), {
53
53
  label: '',
54
54
  required: false,
55
55
  disabled: false,
56
+ small: false,
56
57
  });
57
58
 
58
59
  const input: Ref<HTMLElement | null> = ref(null);
@@ -5,7 +5,7 @@
5
5
  :model-value="modelValue"
6
6
  :open="isDatalistOpen"
7
7
  @select="onSelect"
8
- @click="setFocus"
8
+ @click="onSelectClick"
9
9
  >
10
10
  <template #input>
11
11
  <div class="tags">
@@ -24,11 +24,8 @@
24
24
  v-model="searching"
25
25
  class="search"
26
26
  :disabled="disabled"
27
- @focus="openDatalist"
28
- @click="openDatalist"
27
+ @input="onInput"
29
28
  @keyup.down="openDatalist"
30
- @input="openDatalist"
31
- @keyup.backspace="onBackspace"
32
29
  />
33
30
  </div>
34
31
 
@@ -37,16 +34,8 @@
37
34
  <X class="icon icon-clear" @click.stop="clearInput" />
38
35
  </template>
39
36
  <template v-else>
40
- <ChevronUp
41
- v-if="isDatalistOpen"
42
- class="icon icon-toggle-button"
43
- @click="closeDatalist"
44
- />
45
- <ChevronDown
46
- v-else
47
- class="icon icon-toggle-button"
48
- @click="openDatalist"
49
- />
37
+ <ChevronUp v-if="isDatalistOpen" class="icon icon-toggle-button" />
38
+ <ChevronDown v-else class="icon icon-toggle-button" />
50
39
  </template>
51
40
  </span>
52
41
  </template>
@@ -67,7 +56,7 @@ import { InputOption } from '@/models';
67
56
  import { searchString } from '@/helpers';
68
57
  import { FieldComponentProps } from '@/plugins/field/field-component.model';
69
58
  import RawSelect from './RawSelect.vue';
70
- import { onClickOutside } from '@vueuse/core';
59
+ import { onClickOutside, onKeyDown, onKeyUp } from '@vueuse/core';
71
60
 
72
61
  export interface Props extends FieldComponentProps {
73
62
  options: InputOption[] | string[];
@@ -80,9 +69,16 @@ const props = withDefaults(defineProps<Props>(), {
80
69
  required: false,
81
70
  });
82
71
 
83
- const inputElement: Ref<HTMLElement | null> = ref(null);
72
+ const emits = defineEmits<{
73
+ select: [option: InputOption | string];
74
+ input: [e: InputEvent];
75
+ }>();
76
+
77
+ const modelValue = defineModel<string[] | null>('modelValue', { default: [] });
84
78
 
79
+ const inputElement: Ref<HTMLElement | null> = ref(null);
85
80
  const isDatalistOpen = ref(false);
81
+ const searching = ref('');
86
82
 
87
83
  const closeDatalist = () => (isDatalistOpen.value = false);
88
84
  const openDatalist = () => {
@@ -91,13 +87,9 @@ const openDatalist = () => {
91
87
  }
92
88
  };
93
89
 
94
- const searching = ref('');
95
-
96
- const modelValue = defineModel<string[] | null>('modelValue', { default: [] });
97
-
98
- onClickOutside(inputElement, closeDatalist, {
99
- ignore: ['.datalist-option', '.icon-toggle-button'],
100
- });
90
+ const onBlur = () => {
91
+ closeDatalist();
92
+ };
101
93
 
102
94
  const onBackspace = () => {
103
95
  if (
@@ -107,6 +99,21 @@ const onBackspace = () => {
107
99
  )
108
100
  modelValue.value.splice(-1);
109
101
  };
102
+ onClickOutside(inputElement, closeDatalist, {
103
+ ignore: ['.datalist-option', '.icon-toggle-button'],
104
+ });
105
+ onKeyUp('Escape', closeDatalist);
106
+ onKeyUp('Tab', closeDatalist);
107
+
108
+ onKeyDown('Backspace', onBackspace);
109
+
110
+ const onSelectClick = () => {
111
+ isDatalistOpen.value = !isDatalistOpen.value;
112
+ };
113
+ const onInput = (e: InputEvent) => {
114
+ emits('input', e);
115
+ openDatalist();
116
+ };
110
117
 
111
118
  const setFocus = () => {
112
119
  if (inputElement.value) {
@@ -122,8 +129,8 @@ const onSelect = (option: InputOption | string) => {
122
129
  }
123
130
 
124
131
  searching.value = '';
132
+ emits('select', option);
125
133
  setFocus();
126
- closeDatalist();
127
134
  };
128
135
 
129
136
  const removeOption = (value: string) => {
@@ -3,7 +3,7 @@
3
3
  v-bind="$props"
4
4
  :open="isDatalistOpen"
5
5
  @select="onSelect"
6
- @click="setFocus"
6
+ @click="onSelectClick"
7
7
  >
8
8
  <template #input>
9
9
  <input
@@ -15,17 +15,11 @@
15
15
  :placeholder="placeholder"
16
16
  :required="required"
17
17
  :disabled="disabled"
18
- @focus="openDatalist"
19
- @click="openDatalist"
20
18
  @keyup.down="openDatalist"
21
19
  />
22
20
  <span class="icon-toggle-container">
23
- <ChevronUp
24
- v-if="isDatalistOpen"
25
- class="icon-toggle-button"
26
- @click="closeDatalist"
27
- />
28
- <ChevronDown v-else class="icon-toggle-button" @click="openDatalist" />
21
+ <ChevronUp v-if="isDatalistOpen" class="icon-toggle-button" />
22
+ <ChevronDown v-else class="icon-toggle-button" />
29
23
  </span>
30
24
  </template>
31
25
  </RawSelect>
@@ -33,12 +27,12 @@
33
27
 
34
28
  <script lang="ts" setup>
35
29
  import { ChevronDown, ChevronUp } from 'lucide-vue-next';
36
- import { computed, Ref, ref } from 'vue';
30
+ import { computed, Ref, ref, watch } from 'vue';
37
31
  import _ from 'lodash';
38
32
  import { InputOption } from '@/models';
39
33
  import { FieldComponentProps } from '@/plugins/field/field-component.model';
40
34
  import RawSelect from './RawSelect.vue';
41
- import { onClickOutside } from '@vueuse/core';
35
+ import { onClickOutside, onKeyUp } from '@vueuse/core';
42
36
 
43
37
  export interface Props extends FieldComponentProps {
44
38
  options: InputOption[];
@@ -55,10 +49,13 @@ const props = withDefaults(defineProps<Props>(), {
55
49
  open: false,
56
50
  });
57
51
 
52
+ const emits = defineEmits<{
53
+ select: [value: any];
54
+ }>();
55
+
58
56
  const modelValue = defineModel<any>('modelValue', { required: true });
59
57
 
60
58
  const inputElement: Ref<HTMLElement | null> = ref(null);
61
-
62
59
  const isDatalistOpen = ref(props.open);
63
60
 
64
61
  const closeDatalist = () => {
@@ -71,13 +68,11 @@ const openDatalist = () => {
71
68
  }
72
69
  };
73
70
 
74
- const emits = defineEmits<{
75
- select: [value: any];
76
- }>();
77
-
78
71
  onClickOutside(inputElement, closeDatalist, {
79
72
  ignore: ['.datalist-option', '.icon-toggle-button'],
80
73
  });
74
+ onKeyUp('Escape', closeDatalist);
75
+ onKeyUp('Tab', closeDatalist);
81
76
 
82
77
  const displayValue = computed(() => {
83
78
  const option = props.options.find((o) =>
@@ -100,6 +95,13 @@ const onSelect = (option: any) => {
100
95
  closeDatalist();
101
96
  };
102
97
 
98
+ const onSelectClick = () => {
99
+ isDatalistOpen.value = !isDatalistOpen.value;
100
+ if (isDatalistOpen) {
101
+ setFocus();
102
+ }
103
+ };
104
+
103
105
  defineExpose({
104
106
  setFocus,
105
107
  });
@@ -10,7 +10,8 @@
10
10
  :required="required"
11
11
  :small="small"
12
12
  @input="onInput"
13
- @focus="onFocus"
13
+ @click="onClick"
14
+ @keyup.down="openDatalist"
14
15
  >
15
16
  <template #icon-start>
16
17
  <slot name="icon-start"></slot>
@@ -58,7 +59,7 @@ import RawInputText from '@/components/form/RawInputText.vue';
58
59
  import { InputOption, InputType } from '@/models';
59
60
  import { ChevronDown, ChevronUp, X } from 'lucide-vue-next';
60
61
  import { FieldComponentProps } from '@/plugins/field/field-component.model';
61
- import { MaybeElementRef, onClickOutside } from '@vueuse/core';
62
+ import { MaybeElementRef, onClickOutside, onKeyUp } from '@vueuse/core';
62
63
 
63
64
  export interface Props extends FieldComponentProps {
64
65
  options: InputOption[];
@@ -82,6 +83,10 @@ const rawInput: Ref<HTMLElement | null> = ref(null);
82
83
  const emits = defineEmits<{
83
84
  addNewOption: [newOption: string];
84
85
  select: [option: InputOption];
86
+ blur: [];
87
+ focus: [];
88
+ click: [];
89
+ input: [e: InputEvent];
85
90
  }>();
86
91
 
87
92
  const getValidOptionByLabel = (label: string) =>
@@ -102,6 +107,13 @@ onClickOutside(rawInput as MaybeElementRef, closeDatalist, {
102
107
  ignore: ['.datalist-option', '.icon-toggle-button', '.icon-clear'],
103
108
  });
104
109
 
110
+ const onBlur = () => {
111
+ emits('blur');
112
+ closeDatalist();
113
+ };
114
+ onKeyUp('Escape', onBlur);
115
+ onKeyUp('Tab', onBlur);
116
+
105
117
  const classes = computed(() => {
106
118
  return { 'is-error': props.errors?.length, 'is-disabled': props.disabled };
107
119
  });
@@ -148,8 +160,8 @@ watch(
148
160
  },
149
161
  );
150
162
 
151
- const onFocus = () => {
152
- openDatalist();
163
+ const onClick = () => {
164
+ isDatalistOpen.value = !isDatalistOpen.value;
153
165
  };
154
166
 
155
167
  const onInput = () => {
@@ -21,6 +21,7 @@
21
21
  @blur="$emits('blur')"
22
22
  @input="onInput"
23
23
  @focus="$emits('focus')"
24
+ @click="$emits('click')"
24
25
  />
25
26
  <span class="field__input-icon field__input-icon--end">
26
27
  <slot name="icon-end"></slot>