sprintify-ui 0.2.23 → 0.2.24

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.
@@ -20,6 +20,7 @@ declare const messages: {
20
20
  apply_filters: string;
21
21
  authentication_code: string;
22
22
  autocomplete_placeholder: string;
23
+ bulk_actions: string;
23
24
  cancel: string;
24
25
  city: string;
25
26
  clear: string;
@@ -108,6 +109,7 @@ declare const messages: {
108
109
  apply_filters: string;
109
110
  authentication_code: string;
110
111
  autocomplete_placeholder: string;
112
+ bulk_actions: string;
111
113
  cancel: string;
112
114
  city: string;
113
115
  clear: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.2.23",
3
+ "version": "0.2.24",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -25,6 +25,7 @@ const props = withDefaults(
25
25
  size?: 'sm' | 'base' | 'lg';
26
26
  icon?: string;
27
27
  wrap?: boolean;
28
+ bordered?: boolean;
28
29
  }>(),
29
30
  {
30
31
  contrast: 'high',
@@ -37,10 +38,18 @@ const props = withDefaults(
37
38
  const colorStyle = computed((): Record<string, string> => {
38
39
  const config = getColorConfig(props.color, props.contrast == 'high');
39
40
 
40
- return {
41
+ const styles: Record<string, string> = {
41
42
  backgroundColor: config.backgroundColor,
42
43
  color: config.textColor,
43
44
  };
45
+
46
+ if (props.bordered) {
47
+ styles.borderColor = config.borderColor;
48
+ styles.borderWidth = '1px';
49
+ styles.borderStyle = 'solid';
50
+ }
51
+
52
+ return styles;
44
53
  });
45
54
 
46
55
  const wrapClasses = computed(() => {
@@ -11,18 +11,34 @@
11
11
  :class="{ 'col-span-1': !compactLayout, 'col-span-2': compactLayout }"
12
12
  >
13
13
  <!-- Header -->
14
- <div class="mb-4 flex space-x-2 empty:mb-0">
14
+ <div
15
+ class="flex space-x-2 empty:mb-0"
16
+ :class="{
17
+ 'mb-2.5': size == 'sm',
18
+ 'mb-4': size == 'md',
19
+ }"
20
+ >
15
21
  <!-- Search bar -->
16
22
  <div
17
23
  v-if="searchable"
18
24
  class="grow"
19
25
  >
20
- <div class="relative h-11">
26
+ <div
27
+ class="relative"
28
+ :class="{
29
+ 'h-9': size == 'sm',
30
+ 'h-11': size == 'md',
31
+ }"
32
+ >
21
33
  <div
22
34
  class="pointer-events-none absolute left-0 top-0 flex h-full items-center justify-center pl-2.5"
23
35
  >
24
36
  <BaseIcon
25
- class="h-6 w-6 text-slate-400"
37
+ class="text-slate-400"
38
+ :class="{
39
+ 'h-5 w-5': size == 'sm',
40
+ 'h-6 w-6': size == 'md',
41
+ }"
26
42
  icon="heroicons:magnifying-glass"
27
43
  />
28
44
  </div>
@@ -30,7 +46,11 @@
30
46
  ref="searchInput"
31
47
  v-model="searchQuery"
32
48
  type="text"
33
- class="h-11 w-full overflow-hidden rounded-md border border-slate-300 bg-white pl-10 pr-9 shadow-sm"
49
+ class="w-full h-full py-0 overflow-hidden rounded-md border border-slate-300 bg-white shadow-sm"
50
+ :class="{
51
+ 'pl-9 pr-8': size == 'sm',
52
+ 'pl-10 pr-9': size == 'md',
53
+ }"
34
54
  :placeholder="t('sui.autocomplete_placeholder')"
35
55
  @input="onSearch"
36
56
  >
@@ -41,13 +61,14 @@
41
61
  <button
42
62
  type="button"
43
63
  class="flex appearance-none items-center rounded p-1 hover:bg-slate-100"
44
- @click="
45
- searchQuery = '';
46
- onSearch('');
47
- "
64
+ @click="searchQuery = ''; onSearch('');"
48
65
  >
49
66
  <BaseIcon
50
- class="h-6 w-6 text-slate-500"
67
+ class="text-slate-500"
68
+ :class="{
69
+ 'h-5 w-5': size == 'sm',
70
+ 'h-6 w-6': size == 'md',
71
+ }"
51
72
  icon="heroicons:x-mark"
52
73
  />
53
74
  </button>
@@ -62,6 +83,7 @@
62
83
  >
63
84
  <BaseDataIteratorSectionButton
64
85
  :section="section"
86
+ :size="size"
65
87
  @open="openSection(i)"
66
88
  />
67
89
  </template>
@@ -71,13 +93,18 @@
71
93
  <BaseMenu
72
94
  v-if="actions && actions.length"
73
95
  tw-menu="w-52"
96
+ :size="size == 'sm' ? 'xs' : 'sm'"
74
97
  :items="actions"
75
98
  >
76
99
  <template #button="{ open }">
77
100
  <div
78
- class="flex h-11 w-11 items-center justify-center rounded-md border border-slate-300 bg-white shadow-sm duration-150 hover:bg-slate-50"
101
+ class="flex items-center justify-center rounded-md border border-slate-300 bg-white shadow-sm duration-150 hover:bg-slate-50"
79
102
  :class="[
80
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
+ }
81
108
  ]"
82
109
  >
83
110
  <BaseIcon icon="heroicons-solid:dots-vertical" />
@@ -277,6 +304,14 @@ const props = defineProps({
277
304
  type: String as PropType<'default' | 'compact'>,
278
305
  },
279
306
 
307
+ /**
308
+ * Overall size and spacing of the component
309
+ */
310
+ size: {
311
+ default: 'md',
312
+ type: String as PropType<'sm' | 'md'>,
313
+ },
314
+
280
315
  /**
281
316
  * Sections
282
317
  */
@@ -334,6 +369,8 @@ const searchQuery = ref('');
334
369
 
335
370
  let lastUrl = '';
336
371
  let lastQueryHash = '';
372
+
373
+ // eslint-disable-next-line vue/no-setup-props-destructure
337
374
  const query = ref<DataTableQuery>(cloneDeep(props.defaultQuery));
338
375
  const slots = useSlots();
339
376
 
@@ -705,7 +742,7 @@ const paginationStart = computed(() => {
705
742
 
706
743
  return (
707
744
  paginationMetadata.value.per_page *
708
- (paginationMetadata.value.current_page - 1) +
745
+ (paginationMetadata.value.current_page - 1) +
709
746
  1
710
747
  );
711
748
  });
@@ -756,7 +793,7 @@ const sectionsInternal = computed<DataIteratorSection[]>(() => {
756
793
  name: 'filters',
757
794
  title: t('sui.filters'),
758
795
  closeText: t('sui.apply_filters'),
759
- icon: 'heroicons:adjustments-horizontal-solid',
796
+ icon: 'heroicons:adjustments-horizontal',
760
797
  opened: true,
761
798
  },
762
799
  ...sections,
@@ -1,7 +1,12 @@
1
1
  <template>
2
2
  <button
3
- class="btn flex h-11 items-center justify-center py-1 text-base shadow-sm"
4
- :class="[width > 600 ? 'px-4' : 'px-3.5']"
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
+ }]"
5
10
  type="button"
6
11
  @click="open()"
7
12
  >
@@ -12,12 +17,17 @@
12
17
  <span
13
18
  v-if="section.title && width > 600"
14
19
  class="ml-2 whitespace-pre text-sm"
15
- >{{ section.title }}</span>
20
+ >
21
+ {{ section.title }}
22
+ </span>
16
23
  <BaseBadge
17
24
  v-if="section.count"
18
25
  class="ml-2"
19
- :label="section.count"
20
- />
26
+ color="blue"
27
+ :size="size == 'sm' ? 'sm' : 'base'"
28
+ >
29
+ {{ section.count }}
30
+ </BaseBadge>
21
31
  </button>
22
32
  </template>
23
33
 
@@ -28,6 +38,7 @@ import BaseBadge from './BaseBadge.vue';
28
38
 
29
39
  defineProps<{
30
40
  section: DataIteratorSection;
41
+ size: 'sm' | 'md';
31
42
  }>();
32
43
 
33
44
  const emit = defineEmits<{
@@ -6,17 +6,20 @@ import BaseBadge from './BaseBadge.vue';
6
6
  import BaseAppNotifications from './BaseAppNotifications.vue';
7
7
  import BaseAppDialogs from './BaseAppDialogs.vue';
8
8
  import { onBeforeUnmount } from 'vue';
9
+ import { DateTime } from 'luxon';
9
10
 
10
11
  export default {
11
12
  title: 'Data/BaseDataTable',
12
13
  component: BaseDataTable,
13
14
  argTypes: {
14
15
  layout: {
15
- control: {
16
- type: 'select',
17
- options: ['default', 'compact'],
18
- },
16
+ control: { type: 'select' },
17
+ options: ['default', 'compact'],
19
18
  },
19
+ size: {
20
+ control: { type: 'select' },
21
+ options: ['sm', 'md'],
22
+ }
20
23
  },
21
24
  args: {
22
25
  url: 'https://effettandem.com/api/content/articles',
@@ -50,21 +53,35 @@ const template = `
50
53
  >
51
54
  <div class="max-w-[200px]">
52
55
  <div class="font-medium text-slate-900 leading-tight truncate">
53
- {{ row.title }}
56
+ {{ row.title.slice(0, 30) }}...
54
57
  </div>
55
- <p class="text-xs leading-tight mt-1 text-slate-500 line-clamp-2">
56
- {{ row.subtitle }}
57
- </p>
58
- </div>
59
58
  </BaseTableColumn>
60
59
 
61
60
  <BaseTableColumn
62
61
  v-slot="{ row }"
63
- label="VIP"
62
+ label="URL"
63
+ field="website_url"
64
+ sortable
65
+ >
66
+ <a :href="row.website_url" target="_blank" class="btn btn-xs whitespace-pre space-x-1.5 shadow-sm">
67
+ <span class="whitespace-pre">Open website</span>
68
+ <BaseIcon icon="heroicons:arrow-top-right-on-square-20-solid" class="w-4 h-4 inline-block" />
69
+ </a>
70
+ </BaseTableColumn>
71
+
72
+ <BaseTableColumn
73
+ v-slot="{ row }"
74
+ label="Access Level"
64
75
  field="access_level"
65
76
  sortable
66
77
  >
67
- <BaseBoolean :model-value="row.access_level == 'vip'" />
78
+ <BaseBadge
79
+ contrast="low"
80
+ :color="row.access_level == 'public' ? 'green' : 'gray'"
81
+ bordered
82
+ >
83
+ {{ row.access_level }}
84
+ </BaseBadge>
68
85
  </BaseTableColumn>
69
86
 
70
87
  <BaseTableColumn
@@ -76,9 +93,9 @@ const template = `
76
93
  :toggle="true"
77
94
  :toggle-default="false"
78
95
  >
79
- <div class="whitespace-pre">
96
+ <BaseBadge color="blue" contrast="low" bordered>
80
97
  {{ Math.round(row.votes_avg_score) }} / 5
81
- </div>
98
+ </BaseBadge>
82
99
  </BaseTableColumn>
83
100
 
84
101
  <BaseTableColumn
@@ -90,40 +107,38 @@ const template = `
90
107
  :toggle="true"
91
108
  sortable
92
109
  >
93
- <BaseBadge>
94
- <span class="whitespace-pre">{{ row.owner?.name }}</span>
95
- </BaseBadge>
110
+ <span class="text-slate-600 whitespace-pre">{{ row.owner?.name }}</span>
96
111
  </BaseTableColumn>
97
112
 
98
113
  <BaseTableColumn
99
114
  v-slot="{ row }"
100
- label="Select"
101
- :clickable="false"
102
- padding="0px"
115
+ label="Created at"
116
+ field="created_at"
117
+ sortable
103
118
  >
104
- <select class="select border border-slate-300 rounded text-xs">
105
- <option>Option 1</option>
106
- </select>
119
+ <p class="text-slate-600 text-sm">{{ DateTime.fromISO(row.created_at).toLocaleString() }}</p>
107
120
  </BaseTableColumn>
108
121
 
109
122
  <BaseTableColumn
110
123
  v-slot="{ row }"
111
- label="Access level"
112
- field="access_level"
113
- sortable
124
+ label="Select"
125
+ :clickable="false"
126
+ padding="0px"
114
127
  >
115
- <BaseBadge>
116
- {{ row.access_level }}
117
- </BaseBadge>
128
+ <select class="select border-none text-sm rounded text-slate-600 focus:z-10 focus:ring-2 bg-transparent">
129
+ <option>Option 1</option>
130
+ <option>Option 2</option>
131
+ </select>
118
132
  </BaseTableColumn>
119
133
 
120
134
  <template #detail="{ row }">
121
- <div class="p-4 text-sm">
122
- <p>ID : {{ row.id }}</p>
123
- <p>Created at : {{ row.created_at }}</p>
135
+ <div class="p-4 text-xs">
136
+ <p>ID : <span class="font-mono">{{ row.id }}</span></p>
137
+ <p>Created at : <span class="font-mono">{{ row.created_at }}</span></p>
124
138
  </div>
125
139
  </template>
126
140
 
141
+
127
142
  <template #test>
128
143
  <div>
129
144
  Section Test
@@ -189,7 +204,7 @@ const templateSetup = (args) => {
189
204
  lastFetch.value = new Date();
190
205
  }
191
206
 
192
- return { args, onCellClick, ownerIsVisible: true, onFetch, lastFetch };
207
+ return { args, onCellClick, ownerIsVisible: true, onFetch, lastFetch, DateTime };
193
208
  };
194
209
 
195
210
  const Template = (args) => ({
@@ -219,12 +234,12 @@ Demo.args = {
219
234
  actions: [
220
235
  {
221
236
  label: 'Open Google',
222
- icon: 'heroicons:link',
237
+ icon: 'heroicons:link-20-solid',
223
238
  href: 'https://google.com',
224
239
  },
225
240
  {
226
241
  label: 'Export',
227
- icon: 'heroicons:table-cells',
242
+ icon: 'heroicons:arrow-down-tray',
228
243
  action: () => alert('export!'),
229
244
  },
230
245
  { line: true },
@@ -262,6 +277,21 @@ Demo.args = {
262
277
  ],
263
278
  };
264
279
 
280
+ export const Small = Template.bind({});
281
+ Small.args = {
282
+ editUrl() {
283
+ return '/';
284
+ },
285
+ deleteUrl() {
286
+ return '/';
287
+ },
288
+ size: 'sm',
289
+ searchable: true,
290
+ toggleable: true,
291
+ actions: [],
292
+ };
293
+
294
+
265
295
  const VIfOnBaseTableColumn = (args) => ({
266
296
  components: templateComponents,
267
297
  setup() {
@@ -338,4 +368,4 @@ Simple.args = {
338
368
  searchable: false,
339
369
  toggleable: false,
340
370
  actions: [],
341
- };
371
+ };
@@ -8,6 +8,7 @@
8
8
  :actions="actions"
9
9
  :history-mode="historyMode"
10
10
  :layout="layout"
11
+ :size="size"
11
12
  :sections="sectionsInternal"
12
13
  :scroll-top-on-fetch="maxHeight ? false : scrollTopOnFetch"
13
14
  @fetch="onFetch"
@@ -40,7 +41,7 @@
40
41
  }}.</span>
41
42
  <button
42
43
  type="button"
43
- class="mr-3 inline text-slate-700 underline"
44
+ class="mr-3 inline text-slate-800 underline underline-offset-1 decoration-slate-400 decoration-2 decoration-dashed"
44
45
  @click="uncheckAll()"
45
46
  >
46
47
  {{ t('sui.deselect_all') }}
@@ -53,12 +54,19 @@
53
54
  >
54
55
  <template #button="{ open }">
55
56
  <div
56
- class="flex h-10 w-10 items-center justify-center rounded-full border border-slate-300 bg-white duration-150 hover:bg-slate-50"
57
+ class="flex h-10 px-4 items-center justify-center btn pl-3"
57
58
  :class="[
58
59
  open ? 'ring-2 ring-primary-500 ring-offset-2' : false,
59
60
  ]"
60
61
  >
61
- <BaseIcon icon="heroicons-solid:dots-vertical" />
62
+ <BaseIcon
63
+ icon="heroicons-solid:dots-vertical"
64
+ class="mr-2"
65
+ />
66
+
67
+ <span class="font-semibold">
68
+ {{ t('sui.bulk_actions') }}
69
+ </span>
62
70
  </div>
63
71
  </template>
64
72
  </BaseMenu>
@@ -79,6 +87,7 @@
79
87
  :sort-direction="sortDirection"
80
88
  :max-height="maxHeight"
81
89
  :visible-columns="visibleColumns"
90
+ :size="size"
82
91
  @update:checked-rows="onCheckedRowsUpdate"
83
92
  @sort="onSortChange"
84
93
  @cell-click="onCellClick"
@@ -103,16 +112,28 @@
103
112
  <BaseDataTableRowAction
104
113
  :row="row"
105
114
  :row-action="rowAction"
115
+ :size="size"
106
116
  />
107
117
  </template>
108
118
  <BaseMenu
109
119
  v-if="showRowActionMenu"
110
120
  :items="rowActionMenuItems(row)"
111
121
  size="sm"
112
- button-class="btn flex h-8 w-8 p-0 items-center justify-center"
122
+ :tw-button="[
123
+ 'btn flex p-0 items-center justify-center',
124
+ {
125
+ 'py-1.5 px-2': size == 'sm',
126
+ 'p-2': size == 'md'
127
+ }
128
+ ]"
113
129
  >
114
130
  <template #button>
115
- <BaseIcon icon="heroicons-solid:dots-vertical" />
131
+ <BaseIcon
132
+ icon="heroicons-solid:dots-vertical"
133
+ :class="{
134
+ 'h-3 w-3': size == 'sm'
135
+ }"
136
+ />
116
137
  </template>
117
138
  </BaseMenu>
118
139
  </div>
@@ -416,12 +437,23 @@ const props = defineProps({
416
437
 
417
438
  /**
418
439
  * Layout type
440
+ *
441
+ * default: Layout with a sidebar
442
+ * compact: Layout without a sidebar, this is the default layout for mobile, even if you don't specify it
419
443
  */
420
444
  layout: {
421
445
  default: 'default',
422
446
  type: String as PropType<'default' | 'compact'>,
423
447
  },
424
448
 
449
+ /**
450
+ * Overall size and spacing of the component
451
+ */
452
+ size: {
453
+ default: 'md',
454
+ type: String as PropType<'sm' | 'md'>,
455
+ },
456
+
425
457
  sections: {
426
458
  default: undefined,
427
459
  type: Array as PropType<DataIteratorSection[]>,
@@ -665,7 +697,7 @@ const sectionsInternal = computed<DataIteratorSection[]>(() => {
665
697
  ...sections,
666
698
  {
667
699
  name: 'columns',
668
- icon: 'heroicons:table-cells-20-solid',
700
+ icon: 'heroicons:view-columns',
669
701
  title: t('sui.columns'),
670
702
  closeText: t('sui.apply'),
671
703
  opened: false,
@@ -2,33 +2,52 @@
2
2
  <button
3
3
  v-if="rowAction.action"
4
4
  type="button"
5
- class="btn btn-white border border-slate-300 p-2 shadow-sm"
5
+ class="btn btn-white border border-slate-300 shadow-sm"
6
+ :class="{
7
+ 'py-1.5 px-2': size === 'sm',
8
+ 'p-2': size === 'md',
9
+ }"
6
10
  :disabled="rowAction.disabled && rowAction.disabled(row)"
7
11
  @click="rowAction.action ? rowAction.action(row) : null"
8
12
  >
9
13
  <BaseIcon
10
14
  :icon="rowAction.icon"
11
15
  class="text-slate-500"
16
+ :class="{
17
+ 'h-3 w-3': size == 'sm'
18
+ }"
12
19
  />
13
20
  </button>
14
21
  <RouterLink
15
22
  v-else-if="rowAction.to"
16
23
  :to="rowAction.to(row)"
17
24
  :disabled="rowAction.disabled && rowAction.disabled(row)"
18
- class="btn btn-white border border-slate-300 p-2 shadow-sm hover:bg-slate-100"
25
+ class="btn btn-white border border-slate-300 shadow-sm hover:bg-slate-100"
26
+ :class="{
27
+ 'py-1.5 px-2': size === 'sm',
28
+ 'p-2': size === 'md',
29
+ }"
19
30
  >
20
31
  <BaseIcon
21
32
  :icon="rowAction.icon"
22
33
  class="text-slate-500"
34
+ :class="{
35
+ 'h-3 w-3': size == 'sm'
36
+ }"
23
37
  />
24
38
  </RouterLink>
25
39
  </template>
26
40
 
27
41
  <script lang="ts" setup>
28
42
  import { CollectionItem, RowAction } from '@/types';
43
+ import { BaseIcon } from '.';
29
44
 
30
- defineProps<{
45
+ withDefaults(defineProps<{
31
46
  row: CollectionItem;
32
47
  rowAction: RowAction;
33
- }>();
48
+ size?: 'sm' | 'md';
49
+ }>(), {
50
+ size: 'md',
51
+ });
52
+
34
53
  </script>
@@ -13,6 +13,10 @@ export default {
13
13
  control: { type: 'select' },
14
14
  options: ['bottom-left', 'bottom-right'],
15
15
  },
16
+ size: {
17
+ control: { type: 'select' },
18
+ options: ['xs', 'sm', 'md'],
19
+ }
16
20
  },
17
21
  };
18
22