sprintify-ui 0.6.34 → 0.6.36

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 (57) hide show
  1. package/dist/sprintify-ui.es.js +15345 -15302
  2. package/dist/style.css +1 -1
  3. package/dist/tailwindcss/button.js +2 -2
  4. package/dist/tailwindcss/theme.js +14 -7
  5. package/dist/types/src/components/BaseButtonGroup.vue.d.ts +9 -0
  6. package/dist/types/src/components/BaseDataIterator.vue.d.ts +3 -3
  7. package/dist/types/src/components/BaseDataIteratorSectionButton.vue.d.ts +2 -2
  8. package/dist/types/src/components/BaseDataIteratorSectionColumns.vue.d.ts +8 -8
  9. package/dist/types/src/components/BaseDataTable.vue.d.ts +3 -3
  10. package/dist/types/src/components/BaseDataTableRowAction.vue.d.ts +4 -3
  11. package/dist/types/src/components/BaseDatePicker.vue.d.ts +10 -0
  12. package/dist/types/src/components/BaseDateSelect.vue.d.ts +9 -0
  13. package/dist/types/src/components/BaseRichText.vue.d.ts +9 -0
  14. package/dist/types/src/components/BaseTable.vue.d.ts +3 -3
  15. package/dist/types/src/components/BaseTagAutocomplete.vue.d.ts +11 -2
  16. package/dist/types/src/components/BaseTextareaAutoresize.vue.d.ts +3 -2
  17. package/dist/types/src/components/BaseTimeline.vue.d.ts +1 -1
  18. package/dist/types/src/components/BaseTimelineItem.vue.d.ts +1 -1
  19. package/dist/types/src/stories/PageInputSizes.vue.d.ts +2 -0
  20. package/dist/types/src/types/TimelineItem.d.ts +0 -9
  21. package/dist/types/src/types/index.d.ts +9 -0
  22. package/dist/types/src/utils/slots.d.ts +1 -0
  23. package/package.json +1 -1
  24. package/src/assets/base-rich-text.css +148 -26
  25. package/src/components/BaseButton.vue +3 -2
  26. package/src/components/BaseButtonGroup.vue +37 -9
  27. package/src/components/BaseColor.vue +29 -31
  28. package/src/components/BaseDataIterator.stories.js +8 -1
  29. package/src/components/BaseDataIterator.vue +36 -76
  30. package/src/components/BaseDataIteratorSectionButton.vue +8 -19
  31. package/src/components/BaseDataTable.vue +23 -16
  32. package/src/components/BaseDataTableRowAction.vue +2 -1
  33. package/src/components/BaseDatePicker.stories.js +23 -0
  34. package/src/components/BaseDatePicker.vue +65 -8
  35. package/src/components/BaseDateSelect.stories.js +25 -1
  36. package/src/components/BaseDateSelect.vue +80 -101
  37. package/src/components/BaseDropdownAutocomplete.stories.js +30 -15
  38. package/src/components/BaseHasMany.stories.js +22 -1
  39. package/src/components/BaseInput.stories.js +1 -1
  40. package/src/components/BaseInput.vue +4 -3
  41. package/src/components/BaseMediaPicturesItem.vue +2 -2
  42. package/src/components/BaseRichText.stories.js +6 -0
  43. package/src/components/BaseRichText.vue +12 -2
  44. package/src/components/BaseSelect.vue +5 -0
  45. package/src/components/BaseTable.vue +2 -1
  46. package/src/components/BaseTagAutocomplete.stories.js +1 -1
  47. package/src/components/BaseTagAutocomplete.vue +143 -88
  48. package/src/components/BaseTagAutocompleteFetch.stories.js +22 -1
  49. package/src/components/BaseTextareaAutoresize.vue +7 -7
  50. package/src/components/BaseTimeline.vue +1 -1
  51. package/src/components/BaseTimelineItem.vue +1 -1
  52. package/src/composables/inputSize.ts +5 -1
  53. package/src/stories/InputSizes.stories.js +22 -0
  54. package/src/stories/PageInputSizes.vue +205 -0
  55. package/src/types/TimelineItem.ts +0 -9
  56. package/src/types/index.ts +11 -1
  57. package/src/utils/slots.ts +13 -0
@@ -14,101 +14,55 @@
14
14
  <div
15
15
  class="flex space-x-2 empty:mb-0"
16
16
  :class="{
17
- 'mb-2.5': size == 'sm',
18
- 'mb-4': size == 'md',
17
+ 'mb-2.5': sizeInternal.size.value == 'sm',
18
+ 'mb-4': sizeInternal.size.value == 'md',
19
19
  }"
20
20
  >
21
21
  <!-- Search bar -->
22
22
  <div
23
23
  v-if="searchable"
24
- class="grow"
24
+ class="grow min-w-0"
25
25
  >
26
- <div
27
- class="relative"
28
- :class="{
29
- 'h-9': size == 'sm',
30
- 'h-11': size == 'md',
31
- }"
32
- >
33
- <div
34
- class="pointer-events-none absolute left-0 top-0 flex h-full items-center justify-center pl-2.5"
35
- >
36
- <BaseIcon
37
- class="text-slate-400"
38
- :class="{
39
- 'h-5 w-5': size == 'sm',
40
- 'h-6 w-6': size == 'md',
41
- }"
42
- icon="heroicons:magnifying-glass"
43
- />
44
- </div>
45
- <input
46
- ref="searchInput"
47
- v-model="searchQuery"
48
- type="text"
49
- class="w-full h-full py-0 overflow-hidden rounded-md border border-slate-300 bg-white shadow-sm placeholder:font-light placeholder:text-sm focus:ring-blue-300 focus:ring-4"
50
- :class="{
51
- 'pl-9 pr-8': size == 'sm',
52
- 'pl-10 pr-9': size == 'md',
53
- }"
54
- :placeholder="t('sui.autocomplete_placeholder')"
55
- @input="onSearch"
56
- >
57
- <div
58
- v-if="searchQuery"
59
- class="absolute right-0 top-0 flex h-full items-center justify-center p-1"
60
- >
61
- <button
62
- type="button"
63
- class="flex appearance-none items-center rounded p-1 hover:bg-slate-100"
64
- @click="searchQuery = ''; onSearch('');"
65
- >
66
- <BaseIcon
67
- class="text-slate-500"
68
- :class="{
69
- 'h-5 w-5': size == 'sm',
70
- 'h-6 w-6': size == 'md',
71
- }"
72
- icon="heroicons:x-mark"
73
- />
74
- </button>
75
- </div>
76
- </div>
26
+ <BaseInput
27
+ v-model="searchQuery"
28
+ :placeholder="t('sui.autocomplete_placeholder')"
29
+ :size="sizeInternal.size.value"
30
+ class="w-full"
31
+ icon-left="heroicons:magnifying-glass"
32
+ :icon-right="searchQuery ? 'heroicons:x-mark' : undefined"
33
+ @icon-right-click="searchQuery = ''; onSearch('');"
34
+ @update:model-value="onSearch"
35
+ @focus="searchInputFocus = true"
36
+ @blur="searchInputFocus = false"
37
+ />
77
38
  </div>
78
39
 
79
40
  <template v-if="compactLayout">
80
- <template
41
+ <BaseDataIteratorSectionButton
81
42
  v-for="(section, i) in sectionsInternal"
82
43
  :key="section.name"
83
- >
84
- <BaseDataIteratorSectionButton
85
- :section="section"
86
- :size="size"
87
- @open="openSection(i)"
88
- />
89
- </template>
44
+ :section="section"
45
+ :size="sizeInternal.size.value"
46
+ @open="openSection(i)"
47
+ />
90
48
  </template>
91
49
 
92
50
  <!-- Menu -->
93
51
  <BaseMenu
94
52
  v-if="actions && actions.length"
95
53
  tw-menu="w-52"
96
- :size="size == 'sm' ? 'xs' : 'sm'"
54
+ :size="sizeInternal.size.value == 'sm' ? 'xs' : 'sm'"
97
55
  :items="actions"
98
56
  >
99
57
  <template #button="{ open }">
100
- <div
101
- class="flex items-center justify-center rounded-md border border-slate-300 bg-white shadow-sm duration-150 hover:bg-slate-50"
58
+ <BaseButton
59
+ as="div"
60
+ :size="sizeInternal.size.value"
102
61
  :class="[
103
- open ? 'ring-2 ring-primary-500 ring-offset-2' : false,
104
- {
105
- 'h-9 w-9': size == 'sm',
106
- 'h-11 w-11': size == 'md',
107
- }
62
+ open ? 'input-focus' : '',
108
63
  ]"
109
- >
110
- <BaseIcon icon="heroicons-solid:dots-vertical" />
111
- </div>
64
+ icon="heroicons-solid:dots-vertical"
65
+ />
112
66
  </template>
113
67
  </BaseMenu>
114
68
  </div>
@@ -211,6 +165,9 @@
211
165
  /* eslint-disable @typescript-eslint/no-explicit-any */
212
166
  import { t } from '@/i18n';
213
167
  type Direction = 'asc' | 'desc';
168
+ import { Size } from '@/utils/sizes';
169
+ import BaseInput from './BaseInput.vue';
170
+ import { useInputSize } from '@/composables/inputSize';
214
171
 
215
172
  const DEFAULT_QUERY = {
216
173
  page: 1,
@@ -234,6 +191,7 @@ import {
234
191
  import { ActionItem } from '@/types';
235
192
 
236
193
  import BaseMenu from './BaseMenu.vue';
194
+ import BaseButton from './BaseButton.vue';
237
195
  import BasePagination from './BasePagination.vue';
238
196
  import { config } from '@/index';
239
197
  import { useMutationObserver, useResizeObserver } from '@vueuse/core';
@@ -309,7 +267,7 @@ const props = defineProps({
309
267
  */
310
268
  size: {
311
269
  default: 'md',
312
- type: String as PropType<'sm' | 'md'>,
270
+ type: String as PropType<Size>,
313
271
  },
314
272
 
315
273
  /**
@@ -343,12 +301,14 @@ const emit = defineEmits([
343
301
  ]);
344
302
 
345
303
  const dataIteratorNode = ref<null | HTMLElement>(null);
346
- const searchInput = ref<null | HTMLInputElement>(null);
304
+ const searchInputFocus = ref(false);
347
305
 
348
306
  const route = useRoute();
349
307
  const router = useRouter();
350
308
  const routeName = route.name;
351
309
 
310
+ const sizeInternal = useInputSize(props.size);
311
+
352
312
  let willUnmount = false;
353
313
 
354
314
  onBeforeUnmount(() => {
@@ -595,7 +555,7 @@ function onRouteChange() {
595
555
  query.value = newQuery;
596
556
 
597
557
  // Update search input if not in focus
598
- if (searchInput.value && searchInput.value !== document.activeElement) {
558
+ if (!searchInputFocus.value) {
599
559
  updateSearchInput();
600
560
  }
601
561
 
@@ -1,23 +1,11 @@
1
1
  <template>
2
- <button
3
- class="btn flex items-center justify-center text-base shadow-sm"
4
- :class="[
5
- width > 600 ? 'px-4' : 'px-3.5',
6
- {
7
- 'h-9': size == 'sm',
8
- 'h-11': size == 'md',
9
- }]"
2
+ <BaseButton
3
+ :icon="section.icon"
4
+ :size="size"
10
5
  type="button"
11
6
  @click="open()"
12
7
  >
13
- <BaseIcon
14
- class="h-5 w-5 text-slate-500"
15
- :icon="section.icon"
16
- />
17
- <span
18
- v-if="section.title && width > 600"
19
- class="ml-2 whitespace-pre text-sm"
20
- >
8
+ <span v-if="section.title && width > 600">
21
9
  {{ section.title }}
22
10
  </span>
23
11
  <BaseBadge
@@ -28,17 +16,18 @@
28
16
  >
29
17
  {{ section.count }}
30
18
  </BaseBadge>
31
- </button>
19
+ </BaseButton>
32
20
  </template>
33
21
 
34
22
  <script lang="ts" setup>
35
23
  import { DataIteratorSection } from '@/types';
36
- import { BaseIcon } from '.';
24
+ import BaseButton from './BaseButton.vue';
37
25
  import BaseBadge from './BaseBadge.vue';
26
+ import { Size } from '@/utils/sizes';
38
27
 
39
28
  defineProps<{
40
29
  section: DataIteratorSection;
41
- size: 'sm' | 'md';
30
+ size: Size;
42
31
  }>();
43
32
 
44
33
  const emit = defineEmits<{
@@ -8,7 +8,7 @@
8
8
  :actions="actions"
9
9
  :history-mode="historyMode"
10
10
  :layout="layout"
11
- :size="size"
11
+ :size="sizeInternal.size.value"
12
12
  :sections="sectionsInternal"
13
13
  :scroll-top-on-fetch="maxHeight ? false : scrollTopOnFetch"
14
14
  @fetch="onFetch"
@@ -87,7 +87,7 @@
87
87
  :sort-direction="sortDirection"
88
88
  :max-height="maxHeight"
89
89
  :visible-columns="visibleColumns"
90
- :size="size"
90
+ :size="sizeInternal.size.value"
91
91
  @update:checked-rows="onCheckedRowsUpdate"
92
92
  @sort="onSortChange"
93
93
  @cell-click="onCellClick"
@@ -112,27 +112,19 @@
112
112
  <BaseDataTableRowAction
113
113
  :row="row"
114
114
  :row-action="rowAction"
115
- :size="size"
115
+ :size="sizeInternal.size.value"
116
116
  />
117
117
  </template>
118
118
  <BaseMenu
119
119
  v-if="showRowActionMenu"
120
120
  :items="rowActionMenuItems(row)"
121
- :size="size == 'sm' ? 'xs' : 'sm'"
122
- :tw-button="[
123
- 'btn flex p-0 items-center justify-center',
124
- {
125
- 'btn-xs py-1 px-2': size == 'sm',
126
- 'btn-sm p-2': size == 'md'
127
- }
128
- ]"
121
+ :size="menuSize"
129
122
  >
130
123
  <template #button>
131
- <BaseIcon
124
+ <BaseButton
125
+ as="div"
126
+ :size="menuSize"
132
127
  icon="heroicons-solid:dots-vertical"
133
- :class="{
134
- 'h-3 w-3': size === 'sm',
135
- }"
136
128
  />
137
129
  </template>
138
130
  </BaseMenu>
@@ -261,6 +253,9 @@ import { RouteLocationRaw } from 'vue-router';
261
253
  import BaseMenu from './BaseMenu.vue';
262
254
  import BaseDataTableRowAction from './BaseDataTableRowAction.vue';
263
255
  import { ActionItem } from '@/types';
256
+ import { Size } from '@/utils/sizes';
257
+ import { useInputSize } from '@/composables/inputSize';
258
+ import BaseButton from './BaseButton.vue';
264
259
 
265
260
  const router = useRouter();
266
261
 
@@ -467,7 +462,7 @@ const props = defineProps({
467
462
  */
468
463
  size: {
469
464
  default: 'md',
470
- type: String as PropType<'sm' | 'md'>,
465
+ type: String as PropType<Size>,
471
466
  },
472
467
 
473
468
  sections: {
@@ -494,6 +489,18 @@ const props = defineProps({
494
489
  },
495
490
  });
496
491
 
492
+ const sizeInternal = useInputSize(props.size);
493
+
494
+ const menuSize = computed(() => {
495
+ if (sizeInternal.size.value == 'md') {
496
+ return 'sm';
497
+ }
498
+ if (sizeInternal.size.value == 'sm') {
499
+ return 'xs';
500
+ }
501
+ return 'xs';
502
+ })
503
+
497
504
  const emit = defineEmits([
498
505
  'cell-click',
499
506
  'delete',
@@ -41,11 +41,12 @@
41
41
  <script lang="ts" setup>
42
42
  import { CollectionItem, RowAction } from '@/types';
43
43
  import { BaseIcon } from '.';
44
+ import { Size } from '@/utils/sizes';
44
45
 
45
46
  withDefaults(defineProps<{
46
47
  row: CollectionItem;
47
48
  rowAction: RowAction;
48
- size?: 'sm' | 'md';
49
+ size?: Size,
49
50
  }>(), {
50
51
  size: 'md',
51
52
  });
@@ -3,6 +3,8 @@ import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
3
  import { DateTime } from 'luxon';
4
4
  import { createFieldStory } from '../../.storybook/utils';
5
5
 
6
+ const sizes = ['xs', 'sm', 'md'];
7
+
6
8
  export default {
7
9
  title: 'Form/BaseDatePicker',
8
10
  component: BaseDatePicker,
@@ -17,6 +19,10 @@ export default {
17
19
  control: { type: 'select' },
18
20
  options: ['single', 'multiple', 'range', 'time'],
19
21
  },
22
+ size: {
23
+ control: { type: 'select' },
24
+ options: sizes,
25
+ }
20
26
  },
21
27
  };
22
28
 
@@ -119,6 +125,23 @@ DateTimeConversion.args = {
119
125
  modelValue: '2023-01-05T00:00:00Z',
120
126
  };
121
127
 
128
+ const TemplateSizes = (args) => ({
129
+ components: { BaseDatePicker },
130
+ setup() {
131
+ const value = ref(null);
132
+ const sizes = ['xs', 'sm', 'md'];
133
+ return { args, value, sizes };
134
+ },
135
+ template: `
136
+ <div v-for="size in sizes" :key="size" class="mb-4">
137
+ <p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
138
+ <BaseDatePicker v-model="value" v-bind="args" :size="size" class="w-full"></BaseDatePicker>
139
+ </div>
140
+ `,
141
+ });
142
+
143
+ export const Sizes = TemplateSizes.bind({});
144
+
122
145
  export const Field = createFieldStory({
123
146
  component: BaseDatePicker,
124
147
  componentName: 'BaseDatePicker',
@@ -7,10 +7,10 @@
7
7
  >
8
8
  <div
9
9
  v-if="showInput"
10
- class="pointer-events-none absolute top-0 left-0 flex h-[42px] items-center justify-center pl-2.5"
10
+ :class="iconWrapClasses"
11
11
  >
12
12
  <BaseIcon
13
- class="relative -top-px h-5 w-5 text-slate-400"
13
+ :class="iconClasses"
14
14
  icon="heroicons:calendar"
15
15
  />
16
16
  </div>
@@ -19,21 +19,21 @@
19
19
  type="text"
20
20
  readonly
21
21
  :disabled="disabled"
22
- class="w-full rounded pl-10 pr-8 disabled:cursor-not-allowed disabled:text-slate-300"
23
- :class="[hasErrorInternal ? 'border-red-500' : 'border-slate-300']"
22
+ class="w-full"
23
+ :class="classes"
24
24
  :placeholder="t('sui.click_or_select_date')"
25
25
  >
26
26
  <div
27
27
  v-if="modelValueFormatted && !disabled && showInput"
28
- class="absolute right-0 top-0 flex h-[42px] items-center justify-center p-1"
28
+ class="absolute top-0 right-0 h-full flex p-1"
29
29
  >
30
30
  <button
31
31
  type="button"
32
- class="flex items-center rounded p-1.5 hover:bg-slate-100"
32
+ class="flex justify-center items-center rounded hover:bg-slate-100 aspect-1"
33
33
  @click="clear()"
34
34
  >
35
35
  <BaseIcon
36
- class="h-5 w-5 text-slate-600"
36
+ :class="iconClasses"
37
37
  icon="heroicons:x-mark"
38
38
  />
39
39
  </button>
@@ -54,11 +54,14 @@ import { French } from 'flatpickr/dist/l10n/fr';
54
54
  import { english } from 'flatpickr/dist/l10n/default';
55
55
  import { Instance } from 'flatpickr/dist/types/instance';
56
56
  import { useI18nStore } from '@/stores/i18n';
57
+ import { twMerge } from 'tailwind-merge';
58
+ import { Size, sizes } from '@/utils/sizes';
57
59
 
58
60
  const props = withDefaults(
59
61
  defineProps<{
60
62
  modelValue?: string | null | string[];
61
63
  required?: boolean;
64
+ size?: Size;
62
65
  disabled?: boolean;
63
66
  minDate?: string | Date | null;
64
67
  maxDate?: string | Date | null;
@@ -75,6 +78,7 @@ const props = withDefaults(
75
78
  modelValue: null,
76
79
  required: false,
77
80
  disabled: false,
81
+ size: undefined,
78
82
  minDate: null,
79
83
  maxDate: null,
80
84
  hasError: false,
@@ -100,9 +104,10 @@ const formatInternal = computed(() => {
100
104
  return 'Y-m-d';
101
105
  });
102
106
 
103
- const { hasErrorInternal, emitUpdate } = useField({
107
+ const { hasErrorInternal, emitUpdate, sizeInternal } = useField({
104
108
  name: computed(() => props.name),
105
109
  required: computed(() => props.required),
110
+ size: computed(() => props.size),
106
111
  hasError: computed(() => props.hasError),
107
112
  emit: emit,
108
113
  });
@@ -293,4 +298,56 @@ function init() {
293
298
  function clear() {
294
299
  emitUpdate(null);
295
300
  }
301
+
302
+ const classes = computed(() => {
303
+ const base = 'transition-colors duration-200 input-rounded';
304
+ const focus = 'focus:input-focus'
305
+ const error = hasErrorInternal.value ? 'border-red-500 focus:input-focus-error' : 'border-slate-300';
306
+ const disabled = 'disabled:cursor-not-allowed disabled:text-slate-300';
307
+ const sizeConfig = sizes[sizeInternal.value];
308
+ const padding = {
309
+ 'xs': 'pl-6 pr-5',
310
+ 'sm': 'pl-[2.1rem] pr-6',
311
+ 'md': 'pl-10 pr-7',
312
+ }[sizeInternal.value];
313
+
314
+ const paddingRight = props.modelValue ? padding : 'pr-0';
315
+
316
+ return twMerge(
317
+ base,
318
+ focus,
319
+ error,
320
+ disabled,
321
+ padding,
322
+ paddingRight,
323
+ sizeConfig.height,
324
+ sizeConfig.fontSize,
325
+ );
326
+ });
327
+
328
+ const iconWrapClasses = computed(() => {
329
+ const base = 'pointer-events-none absolute top-0 left-0 h-full flex items-center justify-center';
330
+ const padding = {
331
+ 'xs': 'pl-2',
332
+ 'sm': 'pl-2.5',
333
+ 'md': 'pl-3',
334
+ }[sizeInternal.value];
335
+
336
+ return twMerge(
337
+ base,
338
+ padding
339
+ );
340
+ });
341
+
342
+ const iconClasses = computed(() => {
343
+ const base = '';
344
+ const error = hasErrorInternal.value ? 'text-red-500' : 'text-slate-500';
345
+ const sizeConfig = sizes[sizeInternal.value];
346
+
347
+ return twMerge(
348
+ base,
349
+ error,
350
+ sizeConfig.iconSize,
351
+ );
352
+ });
296
353
  </script>
@@ -2,10 +2,17 @@ import BaseDateSelect from './BaseDateSelect.vue';
2
2
  import ShowValue from '@/../.storybook/components/ShowValue.vue';
3
3
  import { createFieldStory } from '../../.storybook/utils';
4
4
 
5
+ const sizes = ['xs', 'sm', 'md'];
6
+
5
7
  export default {
6
8
  title: 'Form/BaseDateSelect',
7
9
  component: BaseDateSelect,
8
- argTypes: {},
10
+ argTypes: {
11
+ size: {
12
+ control: { type: 'select' },
13
+ options: sizes,
14
+ }
15
+ },
9
16
  };
10
17
 
11
18
  const Template = (args) => ({
@@ -40,6 +47,23 @@ Disabled.args = {
40
47
  disabled: true,
41
48
  };
42
49
 
50
+ const TemplateSizes = (args) => ({
51
+ components: { BaseDateSelect },
52
+ setup() {
53
+ const value = ref(null);
54
+ const sizes = ['xs', 'sm', 'md'];
55
+ return { args, value, sizes };
56
+ },
57
+ template: `
58
+ <div v-for="size in sizes" :key="size" class="mb-4">
59
+ <p class="text-xs text-slate-600 leading-tight mb-1">{{ size }}</p>
60
+ <BaseDateSelect v-model="value" v-bind="args" :size="size" class="w-full"></BaseDateSelect>
61
+ </div>
62
+ `,
63
+ });
64
+
65
+ export const Sizes = TemplateSizes.bind({});
66
+
43
67
  export const Field = createFieldStory({
44
68
  component: BaseDateSelect,
45
69
  componentName: 'BaseDateSelect',