sprintify-ui 0.0.41 → 0.0.43

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 (89) hide show
  1. package/dist/sprintify-ui.es.js +6033 -5518
  2. package/dist/types/src/components/BaseAutocomplete.vue.d.ts +32 -12
  3. package/dist/types/src/components/BaseAutocompleteFetch.vue.d.ts +28 -28
  4. package/dist/types/src/components/BaseBelongsTo.vue.d.ts +35 -35
  5. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +46 -8
  6. package/dist/types/src/components/BaseDatePicker.vue.d.ts +18 -9
  7. package/dist/types/src/components/BaseDateSelect.vue.d.ts +14 -5
  8. package/dist/types/src/components/BaseField.vue.d.ts +151 -0
  9. package/dist/types/src/components/BaseFieldI18n.vue.d.ts +96 -0
  10. package/dist/types/src/components/BaseForm.vue.d.ts +267 -0
  11. package/dist/types/src/components/BaseFormField.d.ts +81 -0
  12. package/dist/types/src/components/BaseHasMany.vue.d.ts +31 -31
  13. package/dist/types/src/components/BaseInput.vue.d.ts +1 -1
  14. package/dist/types/src/components/BaseInputError.vue.d.ts +48 -0
  15. package/dist/types/src/components/BaseInputPercent.vue.d.ts +1 -1
  16. package/dist/types/src/components/BaseLocaleForm.vue.d.ts +420 -0
  17. package/dist/types/src/components/BaseMediaLibrary.vue.d.ts +46 -24
  18. package/dist/types/src/components/BaseNumberForm.vue.d.ts +382 -0
  19. package/dist/types/src/components/BasePassword.vue.d.ts +10 -14
  20. package/dist/types/src/components/BasePasswordForm.vue.d.ts +365 -0
  21. package/dist/types/src/components/BaseRadioGroup.vue.d.ts +23 -4
  22. package/dist/types/src/components/BaseSelect.vue.d.ts +20 -1
  23. package/dist/types/src/components/BaseSwitch.vue.d.ts +155 -23
  24. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +31 -12
  25. package/dist/types/src/components/BaseTagAutocompleteFetch.vue.d.ts +20 -20
  26. package/dist/types/src/components/BaseTextarea.vue.d.ts +9 -0
  27. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +18 -0
  28. package/dist/types/src/components/BaseTextareaForm.vue.d.ts +394 -0
  29. package/dist/types/src/components/index.d.ts +4 -1
  30. package/dist/types/src/composables/field.d.ts +17 -0
  31. package/dist/types/src/index.d.ts +3 -0
  32. package/dist/types/src/types/index.d.ts +11 -0
  33. package/package.json +4 -1
  34. package/src/components/BaseAutocomplete.stories.js +56 -51
  35. package/src/components/BaseAutocomplete.vue +25 -8
  36. package/src/components/BaseAutocompleteFetch.stories.js +67 -65
  37. package/src/components/BaseAutocompleteFetch.vue +9 -29
  38. package/src/components/BaseBelongsTo.stories.js +72 -82
  39. package/src/components/BaseBelongsTo.vue +10 -11
  40. package/src/components/BaseButtonGroup.stories.js +11 -10
  41. package/src/components/BaseButtonGroup.vue +22 -9
  42. package/src/components/BaseCharacterCounter.stories.js +1 -1
  43. package/src/components/BaseDatePicker.stories.js +13 -9
  44. package/src/components/BaseDatePicker.vue +25 -8
  45. package/src/components/BaseDateSelect.stories.js +15 -9
  46. package/src/components/BaseDateSelect.vue +20 -8
  47. package/src/components/BaseField.vue +109 -0
  48. package/src/components/BaseFieldI18n.stories.js +38 -0
  49. package/src/components/BaseFieldI18n.vue +162 -0
  50. package/src/components/BaseFileUploader.stories.js +3 -3
  51. package/src/components/BaseFileUploader.vue +3 -3
  52. package/src/components/BaseForm.vue +298 -0
  53. package/src/components/BaseFormField.ts +117 -0
  54. package/src/components/BaseHasMany.stories.js +25 -10
  55. package/src/components/BaseHasMany.vue +9 -9
  56. package/src/components/BaseInput.stories.js +27 -14
  57. package/src/components/BaseInput.vue +17 -8
  58. package/src/components/BaseInputError.vue +7 -0
  59. package/src/components/BaseInputPercent.stories.js +10 -3
  60. package/src/components/BaseInputPercent.vue +2 -1
  61. package/src/components/BaseLocaleForm.vue +142 -0
  62. package/src/components/BaseMediaLibrary.stories.js +7 -6
  63. package/src/components/BaseMediaLibrary.vue +32 -31
  64. package/src/components/BaseMenu.vue +1 -1
  65. package/src/components/BaseNumberForm.vue +67 -0
  66. package/src/components/BasePassword.stories.js +9 -4
  67. package/src/components/BasePassword.vue +49 -44
  68. package/src/components/BasePasswordForm.vue +59 -0
  69. package/src/components/BaseRadioGroup.stories.js +9 -8
  70. package/src/components/BaseRadioGroup.vue +17 -3
  71. package/src/components/BaseSelect.stories.js +15 -2
  72. package/src/components/BaseSelect.vue +26 -10
  73. package/src/components/BaseSwitch.stories.js +7 -0
  74. package/src/components/BaseSwitch.vue +134 -124
  75. package/src/components/BaseTagAutocomplete.stories.js +21 -14
  76. package/src/components/BaseTagAutocomplete.vue +25 -14
  77. package/src/components/BaseTagAutocompleteFetch.stories.js +37 -21
  78. package/src/components/BaseTagAutocompleteFetch.vue +5 -5
  79. package/src/components/BaseTextarea.stories.js +11 -3
  80. package/src/components/BaseTextarea.vue +20 -6
  81. package/src/components/BaseTextareaAutoresize.stories.js +11 -2
  82. package/src/components/BaseTextareaAutoresize.vue +28 -4
  83. package/src/components/BaseTextareaForm.vue +101 -0
  84. package/src/components/BaseTimeline.vue +1 -1
  85. package/src/components/BaseTimelineItem.vue +4 -4
  86. package/src/components/index.ts +6 -0
  87. package/src/composables/field.ts +100 -0
  88. package/src/index.ts +11 -1
  89. package/src/types/index.ts +12 -0
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <div>
3
- <div class="min-h-[42px] rounded border border-slate-300 p-1">
3
+ <div
4
+ class="min-h-[42px] rounded border p-1"
5
+ :class="[hasErrorInternal ? 'border-red-600' : 'border-slate-300']"
6
+ >
4
7
  <div class="-m-0.5 flex flex-wrap">
5
8
  <div
6
9
  v-for="selection in normalizedModelValue"
@@ -38,8 +41,7 @@
38
41
  :value="keywords"
39
42
  type="text"
40
43
  :placeholder="$t('sui.select_an_item')"
41
- class="h-[32px] w-full min-w-[50px] border-none p-0 pl-1 shadow-none outline-none focus:border-none focus:shadow-none focus:outline-none focus:ring-0"
42
- :class="inputClass"
44
+ class="h-[32px] w-full min-w-[50px] border-none p-0 pl-1 shadow-none outline-none focus:border-none focus:shadow-none focus:outline-none focus:ring-0 disabled:cursor-not-allowed"
43
45
  @focus="onTextFocus"
44
46
  @blur="onTextBlur"
45
47
  @input="onTextInput"
@@ -129,13 +131,14 @@ import { PropType, Ref, ComputedRef } from 'vue';
129
131
  import { NormalizedOption, Option } from '@/types';
130
132
  import { useInfiniteScroll, useMutationObserver } from '@vueuse/core';
131
133
  import { useNotificationsStore } from '@/stores/notifications';
132
- import BaseSkeleton from './BaseSkeleton.vue';
134
+ import BaseSkeleton from '@/components/BaseSkeleton.vue';
133
135
  import { useHasOptions } from '@/composables/hasOptions';
136
+ import { useField } from '@/composables/field';
134
137
 
135
138
  const props = defineProps({
136
139
  modelValue: {
137
140
  required: true,
138
- type: Array as PropType<Option[]>,
141
+ type: [Array, null] as PropType<Option[] | null>,
139
142
  },
140
143
  options: {
141
144
  required: true,
@@ -149,7 +152,7 @@ const props = defineProps({
149
152
  required: true,
150
153
  type: String,
151
154
  },
152
- inputClass: {
155
+ name: {
153
156
  default: undefined,
154
157
  type: String,
155
158
  },
@@ -177,6 +180,10 @@ const props = defineProps({
177
180
  default: undefined,
178
181
  type: Function as PropType<(option: NormalizedOption) => boolean>,
179
182
  },
183
+ hasError: {
184
+ default: false,
185
+ type: Boolean,
186
+ },
180
187
  });
181
188
 
182
189
  const emit = defineEmits([
@@ -186,6 +193,13 @@ const emit = defineEmits([
186
193
  'scrollBottom',
187
194
  ]);
188
195
 
196
+ const { hasErrorInternal, emitUpdate } = useField({
197
+ name: computed(() => props.name),
198
+ required: computed(() => props.required),
199
+ hasError: computed(() => props.hasError),
200
+ emit: emit,
201
+ });
202
+
189
203
  const i18n = useI18n();
190
204
  const notifications = useNotificationsStore();
191
205
 
@@ -377,7 +391,7 @@ const addOption = (option: NormalizedOption) => {
377
391
 
378
392
  beforeAddOption();
379
393
 
380
- emitUpdate([...normalizedModelValue.value, option]);
394
+ update([...normalizedModelValue.value, option]);
381
395
 
382
396
  setKeywords('');
383
397
  };
@@ -405,16 +419,13 @@ const attemptRemoveLastSelection = () => {
405
419
  const removeOption = (option: NormalizedOption) => {
406
420
  let newModelValue = cloneDeep(normalizedModelValue.value);
407
421
  newModelValue = newModelValue.filter((v) => v.value != option.value);
408
- emitUpdate(newModelValue);
422
+ update(newModelValue);
409
423
  };
410
424
 
411
- const emitUpdate = (value: NormalizedOption[]) => {
412
- emit(
413
- 'update:modelValue',
414
- value.map((v) => v.option)
415
- );
425
+ function update(value: NormalizedOption[]) {
426
+ emitUpdate(value.map((v) => v.option));
416
427
  afterUpdate();
417
- };
428
+ }
418
429
 
419
430
  const beforeAddOption = () => {
420
431
  selectionToDelete.value = null;
@@ -1,5 +1,7 @@
1
1
  import BaseTagAutocompleteFetch from './BaseTagAutocompleteFetch.vue';
2
- import BaseApp from './BaseApp.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
+ import { createFieldStory, options } from '../../.storybook/utils';
4
+ import BaseAppNotifications from './BaseAppNotifications.vue';
3
5
 
4
6
  export default {
5
7
  title: 'Form/BaseTagAutocompleteFetch',
@@ -9,25 +11,24 @@ export default {
9
11
  url: 'https://effettandem.com/api/content/articles',
10
12
  labelKey: 'title',
11
13
  valueKey: 'id',
12
- disabled: false,
13
14
  },
14
15
  decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
15
16
  };
16
17
 
17
18
  const Template = (args) => {
18
19
  return {
19
- components: { BaseTagAutocompleteFetch, BaseApp },
20
+ components: { BaseTagAutocompleteFetch, ShowValue, BaseAppNotifications },
20
21
  setup() {
21
22
  const value = ref([]);
22
23
  return { args, value };
23
24
  },
24
25
  template: `
25
- <BaseTagAutocompleteFetch
26
- v-model="value"
27
- v-bind="args"
28
- ></BaseTagAutocompleteFetch>
29
- <p class="mt-5 text-sm">Value: <span class="bg-slate-200 font-mono px-1 py-px rounded">{{ value ?? 'NULL' }}</span></p>
30
- <BaseApp />
26
+ <BaseTagAutocompleteFetch
27
+ v-model="value"
28
+ v-bind="args"
29
+ ></BaseTagAutocompleteFetch>
30
+ <ShowValue :value="value" />
31
+ <BaseAppNotifications />
31
32
  `,
32
33
  };
33
34
  };
@@ -35,12 +36,19 @@ const Template = (args) => {
35
36
  export const Demo = Template.bind({});
36
37
  Demo.args = {};
37
38
 
38
- export const Disabled = Template.bind({});
39
- Disabled.args = {
40
- modelValue: [{ label: 'Dark Maul', value: '1' }],
41
- labelKey: 'label',
42
- valueKey: 'value',
43
- disabled: true,
39
+ export const Disabled = (args) => {
40
+ return {
41
+ components: { BaseTagAutocompleteFetch },
42
+ setup() {
43
+ const value = ref([]);
44
+ return { args, value };
45
+ },
46
+ template: `<BaseTagAutocompleteFetch
47
+ v-bind="args"
48
+ v-model="value"
49
+ :disabled="true"
50
+ ></BaseTagAutocompleteFetch>`,
51
+ };
44
52
  };
45
53
 
46
54
  export const Maximum = Template.bind({});
@@ -50,7 +58,7 @@ Maximum.args = {
50
58
 
51
59
  export const SlotOption = (args) => {
52
60
  return {
53
- components: { BaseTagAutocompleteFetch },
61
+ components: {},
54
62
  setup() {
55
63
  const value = ref([]);
56
64
  return { args, value };
@@ -83,11 +91,13 @@ export const SlotOption = (args) => {
83
91
 
84
92
  export const SlotFooter = (args) => {
85
93
  return {
86
- components: { BaseTagAutocompleteFetch },
94
+ components: {},
87
95
  setup() {
88
96
  const value = ref([]);
89
97
  function onClick() {
90
- alert(1);
98
+ setTimeout(() => {
99
+ alert(1);
100
+ }, 300);
91
101
  }
92
102
  return { args, value, onClick };
93
103
  },
@@ -98,7 +108,7 @@ export const SlotFooter = (args) => {
98
108
  >
99
109
  <template #footer>
100
110
  <div class="text-center p-2 border-t">
101
- <button @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
111
+ <button type="button" @click=onClick class="btn btn-sm w-full btn-slate-200-outline">This is the footer 💯</button>
102
112
  </div>
103
113
  </template>
104
114
  </BaseTagAutocompleteFetch>
@@ -108,7 +118,7 @@ export const SlotFooter = (args) => {
108
118
 
109
119
  export const SlotEmpty = (args) => {
110
120
  return {
111
- components: { BaseTagAutocompleteFetch },
121
+ components: {},
112
122
  setup() {
113
123
  const value = ref([]);
114
124
  return { args, value };
@@ -121,10 +131,16 @@ export const SlotEmpty = (args) => {
121
131
  <template #empty="props">
122
132
  <div>
123
133
  <div v-if="props.firstSearch" class="text-center py-10 p-6">🤓🤓🤓</div>
124
- <div v-else class="text-center p-6">Start your search... 🔎</div>
134
+ <div v-else class="text-center px-6 py-20">Start your search... 🔎</div>
125
135
  </div>
126
136
  </template>
127
137
  </BaseTagAutocompleteFetch>
128
138
  `,
129
139
  };
130
140
  };
141
+
142
+ export const Field = createFieldStory({
143
+ component: BaseTagAutocompleteFetch,
144
+ componentName: 'BaseTagAutocompleteFetch',
145
+ label: 'Name',
146
+ });
@@ -7,7 +7,7 @@
7
7
  :options="options"
8
8
  :value-key="valueKey"
9
9
  :label-key="labelKey"
10
- :input-class="inputClass"
10
+ :has-error="hasError"
11
11
  :max="max"
12
12
  :filter="() => true"
13
13
  @focus="onFocus"
@@ -60,10 +60,6 @@ const props = defineProps({
60
60
  required: true,
61
61
  type: String,
62
62
  },
63
- inputClass: {
64
- default: undefined,
65
- type: String,
66
- },
67
63
  placeholder: {
68
64
  default: undefined,
69
65
  type: String,
@@ -84,6 +80,10 @@ const props = defineProps({
84
80
  default: 'search',
85
81
  type: String,
86
82
  },
83
+ hasError: {
84
+ default: false,
85
+ type: Boolean,
86
+ },
87
87
  });
88
88
 
89
89
  defineEmits(['update:modelValue', 'typing', 'focus', 'scrollBottom']);
@@ -1,11 +1,10 @@
1
+ import { createFieldStory } from '../../.storybook/utils';
1
2
  import BaseTextarea from './BaseTextarea.vue';
2
3
 
3
4
  export default {
4
5
  title: 'Form/BaseTextarea',
5
6
  component: BaseTextarea,
6
7
  args: {
7
- name: 'bio',
8
- required: true,
9
8
  placeholder: 'Describe your complete life in 4 sentences...',
10
9
  },
11
10
  };
@@ -21,15 +20,24 @@ const Template = (args) => ({
21
20
  template: `
22
21
  <form @submit.prevent="" class="border-none">
23
22
  <BaseTextarea v-model="value" v-bind="args" class="w-full"></BaseTextarea>
23
+ <button type="submit" class="btn btn-primary mt-4">Submit</button>
24
24
  </form>
25
25
  `,
26
26
  });
27
27
 
28
28
  export const Demo = Template.bind({});
29
- Demo.args = {};
29
+ Demo.args = {
30
+ required: true,
31
+ };
30
32
 
31
33
  export const Disabled = Template.bind({});
32
34
  Disabled.args = {
33
35
  modelValue: 'Lorem ipsum...',
34
36
  disabled: true,
35
37
  };
38
+
39
+ export const Field = createFieldStory({
40
+ component: BaseTextarea,
41
+ componentName: 'BaseTextarea',
42
+ label: 'Biography',
43
+ });
@@ -2,21 +2,23 @@
2
2
  <textarea
3
3
  :value="modelValue"
4
4
  :type="type"
5
- :name="name"
5
+ :name="nameInternal"
6
6
  :placeholder="placeholder"
7
7
  :disabled="disabled"
8
- :required="required"
8
+ :required="requiredInternal"
9
9
  :rows="rows"
10
- class="mb-0 block rounded border-slate-300 disabled:cursor-not-allowed disabled:text-slate-300"
11
- @input="$emit('update:modelValue', transformInputValue($event))"
10
+ :class="[hasErrorInternal ? 'border-red-500' : 'border-slate-300']"
11
+ class="mb-0 block rounded disabled:cursor-not-allowed disabled:text-slate-300"
12
+ @input="emitUpdate(transformInputValue($event))"
12
13
  />
13
14
  </template>
14
15
 
15
16
  <script lang="ts" setup>
17
+ import { useField } from '@/composables/field';
16
18
  import { get, isString, trim } from 'lodash';
17
19
  import { PropType } from 'vue';
18
20
 
19
- defineProps({
21
+ const props = defineProps({
20
22
  modelValue: {
21
23
  default: undefined,
22
24
  type: String as PropType<string | undefined>,
@@ -53,9 +55,21 @@ defineProps({
53
55
  default: undefined,
54
56
  type: Number,
55
57
  },
58
+ hasError: {
59
+ default: false,
60
+ type: Boolean,
61
+ },
56
62
  });
57
63
 
58
- defineEmits(['update:modelValue']);
64
+ const emit = defineEmits(['update:modelValue']);
65
+
66
+ const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
67
+ useField({
68
+ name: computed(() => props.name),
69
+ required: computed(() => props.required),
70
+ hasError: computed(() => props.hasError),
71
+ emit: emit,
72
+ });
59
73
 
60
74
  function transformInputValue(event: Event | null): string | null {
61
75
  if (event === null) {
@@ -1,4 +1,6 @@
1
1
  import BaseTextareaAutoresize from './BaseTextareaAutoresize.vue';
2
+ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
+ import { createFieldStory } from '../../.storybook/utils';
2
4
 
3
5
  export default {
4
6
  title: 'Form/BaseTextareaAutoresize',
@@ -12,12 +14,13 @@ export default {
12
14
  const Template = (args) => ({
13
15
  components: {
14
16
  BaseTextareaAutoresize,
17
+ ShowValue,
15
18
  },
16
19
  setup() {
17
20
  function onSubmit() {
18
21
  alert('submit');
19
22
  }
20
- const value = ref('');
23
+ const value = ref(null);
21
24
  return { args, value, onSubmit };
22
25
  },
23
26
  template: `
@@ -30,7 +33,7 @@ const Template = (args) => ({
30
33
  ></BaseTextareaAutoresize>
31
34
  </form>
32
35
 
33
- <pre class="mt-4 bg-slate-800 font-light text-xs p-3 rounded whitespace-pre-wrap text-white">{{ {value} }}</pre>
36
+ <ShowValue :value="value" />
34
37
  `,
35
38
  });
36
39
 
@@ -47,3 +50,9 @@ Disabled.args = {
47
50
  modelValue: 'Lorem ipsum...',
48
51
  disabled: true,
49
52
  };
53
+
54
+ export const Field = createFieldStory({
55
+ component: BaseTextareaAutoresize,
56
+ componentName: 'BaseTextareaAutoresize',
57
+ label: 'Biography',
58
+ });
@@ -6,11 +6,15 @@
6
6
  >
7
7
  <textarea
8
8
  :value="modelValue"
9
- :name="name"
9
+ :name="nameInternal"
10
10
  :placeholder="placeholder"
11
11
  :disabled="disabled"
12
+ :required="requiredInternal"
12
13
  class="resize-none"
13
- :class="BASE_TEXTAREA_CLASSES"
14
+ :class="[
15
+ BASE_TEXTAREA_CLASSES,
16
+ hasErrorInternal ? 'border-red-500' : 'border-slate-300',
17
+ ]"
14
18
  :style="{ maxHeight: maxHeight + 'px', gridArea: BASE_GRID_AREA }"
15
19
  rows="1"
16
20
  @input="onInput"
@@ -20,7 +24,10 @@
20
24
  />
21
25
  <div
22
26
  class="invisible whitespace-pre-wrap"
23
- :class="BASE_TEXTAREA_CLASSES"
27
+ :class="[
28
+ BASE_TEXTAREA_CLASSES,
29
+ hasErrorInternal ? 'border-red-500' : 'border-slate-300',
30
+ ]"
24
31
  :style="{
25
32
  content: DIV_CONTENT,
26
33
  maxHeight: maxHeight + 'px',
@@ -33,10 +40,11 @@
33
40
  </template>
34
41
 
35
42
  <script lang="ts" setup>
43
+ import { useField } from '@/composables/field';
36
44
  import { Ref } from 'vue';
37
45
 
38
46
  const BASE_TEXTAREA_CLASSES =
39
- 'py-2 px-3 font-normal text-base disabled:cursor-not-allowed disabled:text-slate-300 font-sans rounded leading-normal tracking-normal border border-slate-300';
47
+ 'py-2 px-3 font-normal text-base disabled:cursor-not-allowed disabled:text-slate-300 font-sans rounded leading-normal tracking-normal border';
40
48
 
41
49
  const BASE_GRID_AREA = '1 / 1 / 2 / 2';
42
50
 
@@ -56,6 +64,10 @@ const props = defineProps({
56
64
  required: true,
57
65
  type: String,
58
66
  },
67
+ required: {
68
+ default: false,
69
+ type: Boolean,
70
+ },
59
71
  maxHeight: {
60
72
  default: 100,
61
73
  type: Number,
@@ -72,10 +84,22 @@ const props = defineProps({
72
84
  default: false,
73
85
  type: Boolean,
74
86
  },
87
+ hasError: {
88
+ default: false,
89
+ type: Boolean,
90
+ },
75
91
  });
76
92
 
77
93
  const emit = defineEmits(['update:modelValue', 'submit', 'focus', 'input']);
78
94
 
95
+ const { nameInternal, requiredInternal, hasErrorInternal, emitUpdate } =
96
+ useField({
97
+ name: computed(() => props.name),
98
+ required: computed(() => props.required),
99
+ hasError: computed(() => props.hasError),
100
+ emit: emit,
101
+ });
102
+
79
103
  const wrapper = ref(null) as Ref<null | HTMLDivElement>;
80
104
 
81
105
  const keys = {} as { [key: string]: boolean };
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <div>
3
+ <BaseInputLabel
4
+ v-if="labelValue"
5
+ :label="labelValue"
6
+ :required="required"
7
+ />
8
+ <BaseTextarea
9
+ :model-value="normalizedModelValue"
10
+ :type="type"
11
+ :name="name"
12
+ :placeholder="placeholder"
13
+ :disabled="disabled"
14
+ :required="required"
15
+ :rows="rows"
16
+ :class="[
17
+ {
18
+ 'border-red-600': hasError(),
19
+ 'border-slate-300': !hasError(),
20
+ },
21
+ ]"
22
+ @update:model-value="inputListener($event)"
23
+ />
24
+ <div class="mt-1 flex flex-wrap sm:space-x-3">
25
+ <BaseInputError v-if="hasError()" class="order-2 grow sm:order-1">
26
+ {{ errorMessage() }}
27
+ </BaseInputError>
28
+ <div
29
+ class="order-1 w-full grow text-left sm:order-2 sm:w-auto sm:text-right"
30
+ >
31
+ <BaseCharacterCounter
32
+ :text="normalizedModelValue"
33
+ :min="min"
34
+ :max="max"
35
+ />
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </template>
40
+
41
+ <script lang="ts">
42
+ import { defineComponent, PropType } from 'vue';
43
+ import BaseFormField from './BaseFormField';
44
+ import { get, isString, trim } from 'lodash';
45
+ import BaseTextarea from './BaseTextarea.vue';
46
+ import BaseInputLabel from './BaseInputLabel.vue';
47
+ import BaseCharacterCounter from './BaseCharacterCounter.vue';
48
+ import BaseInputError from './BaseInputError.vue';
49
+
50
+ export default defineComponent({
51
+ components: {
52
+ BaseTextarea,
53
+ BaseInputLabel,
54
+ BaseCharacterCounter,
55
+ BaseInputError,
56
+ },
57
+ extends: BaseFormField,
58
+ props: {
59
+ modelValue: {
60
+ required: true,
61
+ type: [String, null] as PropType<string | null>,
62
+ },
63
+ type: {
64
+ type: String,
65
+ default: 'text',
66
+ },
67
+ rows: {
68
+ default: 3,
69
+ type: Number,
70
+ },
71
+ min: {
72
+ default: undefined,
73
+ type: Number,
74
+ },
75
+ max: {
76
+ default: undefined,
77
+ type: Number,
78
+ },
79
+ },
80
+ computed: {
81
+ normalizedModelValue(): string {
82
+ if (isString(this.modelValue)) {
83
+ return this.modelValue;
84
+ }
85
+ return '';
86
+ },
87
+ },
88
+ methods: {
89
+ transformInputValue(event: Event | null): string | null {
90
+ if (event === null) {
91
+ return null;
92
+ }
93
+ const value = get(event, 'target.value', null);
94
+ if (!isString(value)) {
95
+ return null;
96
+ }
97
+ return trim(value as string);
98
+ },
99
+ },
100
+ });
101
+ </script>
@@ -5,7 +5,7 @@
5
5
  <div class="relative pb-8">
6
6
  <span
7
7
  v-if="index != items.length - 1"
8
- class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200"
8
+ class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-slate-200"
9
9
  aria-hidden="true"
10
10
  />
11
11
  <BaseTimelineItem :item="item" />
@@ -8,14 +8,14 @@
8
8
  ]"
9
9
  >
10
10
  <BaseIcon
11
- class="w-5 h-5 text-white"
11
+ class="h-5 w-5 text-white"
12
12
  aria-hidden="true"
13
13
  :icon="item.icon"
14
14
  />
15
15
  </span>
16
16
  </div>
17
17
  <div
18
- class="flex justify-between flex-1 min-w-0 space-x-4"
18
+ class="flex min-w-0 flex-1 justify-between space-x-4"
19
19
  :class="{ 'pt-1.5': !item.description }"
20
20
  >
21
21
  <div>
@@ -31,7 +31,7 @@
31
31
  </div>
32
32
  <div
33
33
  v-if="item.date"
34
- class="text-sm text-right text-gray-500 whitespace-nowrap"
34
+ class="whitespace-nowrap text-right text-sm text-slate-500"
35
35
  >
36
36
  <time :datetime="item.date">{{ item.date }}</time>
37
37
  </div>
@@ -74,6 +74,6 @@ const iconBackgroundClass = computed((): string => {
74
74
  if (props.item.color == Colors.success) {
75
75
  return 'bg-green-500';
76
76
  }
77
- return 'bg-gray-500';
77
+ return 'bg-slate-500';
78
78
  });
79
79
  </script>
@@ -25,8 +25,11 @@ import BaseDateSelect from './BaseDateSelect.vue';
25
25
  import BaseDescriptionList from './BaseDescriptionList.vue';
26
26
  import BaseDescriptionListItem from './BaseDescriptionListItem.vue';
27
27
  import BaseDialog from './BaseDialog.vue';
28
+ import BaseField from './BaseField.vue';
29
+ import BaseFieldI18n from './BaseFieldI18n.vue';
28
30
  import BaseFilePicker from './BaseFilePicker.vue';
29
31
  import BaseFileUploader from './BaseFileUploader.vue';
32
+ import BaseForm from './BaseForm.vue';
30
33
  import BaseHasMany from './BaseHasMany.vue';
31
34
  import { Icon as BaseIcon } from '@iconify/vue';
32
35
  import BaseInput from './BaseInput.vue';
@@ -100,8 +103,11 @@ export {
100
103
  BaseDescriptionList,
101
104
  BaseDescriptionListItem,
102
105
  BaseDialog,
106
+ BaseField,
107
+ BaseFieldI18n,
103
108
  BaseFilePicker,
104
109
  BaseFileUploader,
110
+ BaseForm,
105
111
  BaseHasMany,
106
112
  BaseIcon,
107
113
  BaseInput,