sprintify-ui 0.5.2 → 0.5.4

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -6,8 +6,12 @@
6
6
  >
7
7
  <template #button="{ open }">
8
8
  <div
9
- class="relative flex items-center rounded-md p-1.5 hover:bg-slate-100"
10
- :class="[open ? 'bg-slate-100' : '']"
9
+ class="relative flex items-center rounded-md p-1.5"
10
+ :class="[
11
+ dark ? 'hover:bg-slate-700' : 'hover:bg-slate-100',
12
+ open && !dark ? 'bg-slate-100' : '',
13
+ open && dark ? 'bg-slate-700' : '',
14
+ ]"
11
15
  @click="onOpen"
12
16
  >
13
17
  <BaseIcon
@@ -62,11 +66,7 @@
62
66
  class="hover block px-3 py-2 text-center text-sm font-medium text-primary-600 hover:bg-slate-100"
63
67
  :class="[active ? 'bg-slate-100' : '']"
64
68
  >
65
- {{
66
- notificationsConfig.footerLabel
67
- ? notificationsConfig.footerLabel
68
- : t('sui.see_all_notifications')
69
- }}
69
+ {{ notificationsConfig.footerLabel ? notificationsConfig.footerLabel : t('sui.see_all_notifications') }}
70
70
  </div>
71
71
  </RouterLink>
72
72
  </MenuItem>
@@ -12,7 +12,12 @@
12
12
  {{ systemAlert.message }}
13
13
  </BaseSystemAlert>
14
14
 
15
- <BaseNavbar v-bind="navbar">
15
+ <BaseNavbar
16
+ v-bind="navbar"
17
+ :dark="dark"
18
+ :size="size"
19
+ class="shadow"
20
+ >
16
21
  <template #navbar="navbarProps">
17
22
  <slot
18
23
  name="navbar"
@@ -44,10 +49,16 @@ defineOptions({
44
49
  inheritAttrs: false
45
50
  });
46
51
 
47
- const props = defineProps<{
52
+ const props = withDefaults(defineProps<{
48
53
  class?: string | string[];
54
+ dark?: boolean;
49
55
  navbar?: Record<string, unknown>;
50
- }>();
56
+ size?: 'xs' | 'sm' | 'md';
57
+ }>(), {
58
+ class: '',
59
+ navbar: {} as any,
60
+ size: 'md',
61
+ });
51
62
 
52
63
  const classInternal = computed(() => {
53
64
  return twMerge(
@@ -20,12 +20,37 @@ export default {
20
20
  menu: [
21
21
  {
22
22
  label: 'Products',
23
- to: '/',
24
23
  count: 234,
24
+ actions: [
25
+ {
26
+ label: 'New product',
27
+ icon: 'heroicons:plus-20-solid',
28
+ description: 'Create a new product',
29
+ to: '/',
30
+ },
31
+ {
32
+ label: 'All products',
33
+ icon: 'heroicons:academic-cap-20-solid',
34
+ description: 'View all products',
35
+ to: '/',
36
+ },
37
+ ],
25
38
  },
26
39
  {
27
40
  label: 'Users',
28
41
  to: '/setup',
42
+ actions: [
43
+ {
44
+ label: 'New product',
45
+ icon: 'heroicons:plus-20-solid',
46
+ to: '/',
47
+ },
48
+ {
49
+ label: 'All products',
50
+ icon: 'heroicons:academic-cap-20-solid',
51
+ to: '/',
52
+ },
53
+ ],
29
54
  },
30
55
  {
31
56
  label: 'Account',
@@ -44,6 +69,9 @@ export default {
44
69
  to: '/account',
45
70
  icon: 'heroicons:cog',
46
71
  },
72
+ {
73
+ meta: { line: true }
74
+ },
47
75
  {
48
76
  label: 'Logout',
49
77
  action: logout,
@@ -98,16 +126,33 @@ const Template = (args) => ({
98
126
 
99
127
  export const Light = Template.bind({});
100
128
  Light.args = {
101
- navbar: {
102
- dark: false,
103
- },
129
+ dark: false,
104
130
  logoUrl: 'https://sprintify.witify.io/img/logo/logo-side.svg',
105
131
  };
106
132
 
107
133
  export const Dark = Template.bind({});
108
134
  Dark.args = {
109
- navbar: {
110
- dark: true,
111
- },
135
+ dark: true,
112
136
  logoUrl: 'https://sprintify.witify.io/img/logo/logo-side-dark.svg',
113
137
  };
138
+
139
+ export const SizeXS = Template.bind({});
140
+ SizeXS.args = {
141
+ size: 'xs',
142
+ dark: true,
143
+ logoUrl: 'https://sprintify.witify.io/img/logo/logo-side-dark.svg',
144
+ };
145
+
146
+ export const SizeSM = Template.bind({});
147
+ SizeSM.args = {
148
+ size: 'sm',
149
+ dark: true,
150
+ logoUrl: 'https://sprintify.witify.io/img/logo/logo-side-dark.svg',
151
+ };
152
+
153
+ export const SizeMD = Template.bind({});
154
+ SizeMD.args = {
155
+ size: 'md',
156
+ dark: true,
157
+ logoUrl: 'https://sprintify.witify.io/img/logo/logo-side-dark.svg',
158
+ };
@@ -1,6 +1,9 @@
1
1
  <template>
2
- <BaseLayoutStacked :navbar="navbar">
3
- <template #navbar="{ dark }">
2
+ <BaseLayoutStacked
3
+ :size="size"
4
+ :dark="dark"
5
+ >
6
+ <template #navbar>
4
7
  <div class="flex w-full justify-between">
5
8
  <!-- Left -->
6
9
 
@@ -18,10 +21,11 @@
18
21
  </RouterLink>
19
22
 
20
23
  <!-- Links (desktop) -->
21
- <div class="ml-10 hidden space-x-4 md:flex">
24
+ <div class="ml-10 hidden space-x-2 md:flex">
22
25
  <BaseNavbarItem
23
26
  v-for="item in menu"
24
27
  :key="item.label"
28
+ :size="size"
25
29
  :item="item"
26
30
  :dark="dark"
27
31
  />
@@ -35,6 +39,7 @@
35
39
  v-if="notifications"
36
40
  class="mr-4 md:mr-5"
37
41
  :dark="dark"
42
+ :size="size"
38
43
  :notifications-config="notifications"
39
44
  @click="onNotificationClick"
40
45
  @open="onNotificationOpen"
@@ -43,6 +48,7 @@
43
48
  <!-- Profile dropdown -->
44
49
  <BaseMenu
45
50
  tw-menu="w-52"
51
+ :size="size == 'xs' ? 'xs' : 'sm'"
46
52
  class="hidden md:block"
47
53
  :items="userMenu"
48
54
  >
@@ -54,6 +60,7 @@
54
60
  <BaseAvatar
55
61
  :class="dark ? 'text-white' : ''"
56
62
  :user="user"
63
+ :size="size"
57
64
  />
58
65
  </div>
59
66
  </template>
@@ -62,7 +69,7 @@
62
69
  </div>
63
70
  </template>
64
71
 
65
- <template #mobile="{ dark }">
72
+ <template #mobile>
66
73
  <!-- Links mobile -->
67
74
  <div class="space-y-1 p-2 pt-0">
68
75
  <BaseNavbarSideItem
@@ -147,6 +154,14 @@ defineProps({
147
154
  default: undefined,
148
155
  type: Object as PropType<Record<string, unknown>>,
149
156
  },
157
+ dark: {
158
+ default: false,
159
+ type: Boolean,
160
+ },
161
+ size: {
162
+ default: 'md',
163
+ type: String as PropType<'xs' | 'sm' | 'md'>,
164
+ },
150
165
  });
151
166
 
152
167
  function onNotificationClick(notification: Notification) {
@@ -1,29 +1,43 @@
1
1
  <template>
2
2
  <div
3
3
  :style="buttonStyles"
4
- class="rounded leading-tight"
4
+ class="rounded leading-tight hover:bg-slate-100"
5
5
  :class="[active ? 'bg-slate-100' : 'bg-white']"
6
6
  >
7
- <div class="flex items-center">
7
+ <div
8
+ class="flex"
9
+ :class="[description ? 'items-start' : 'items-center']"
10
+ >
8
11
  <BaseIcon
9
12
  v-if="icon"
10
13
  :icon="icon"
11
14
  :class="[iconClasses]"
12
15
  />
13
- <span :class="[textClasses, textColor]">{{ label }}</span>
14
- </div>
15
- <div
16
- v-if="count"
17
- class="relative -top-px ml-[5px]"
18
- >
19
- <BaseCounter
20
- :count="count"
21
- :max-digit="2"
22
- :color="color"
23
- :size="size"
24
- />
16
+ <div class="grow">
17
+ <p :class="['font-medium', textClasses, textColor]">
18
+ {{ label }}
19
+ </p>
20
+ <p
21
+ v-if="description"
22
+ class="text-xs text-slate-500"
23
+ :class="[size == 'xs' ? 'mt-px' : 'mt-0.5']"
24
+ >
25
+ {{ description }}
26
+ </p>
27
+ </div>
25
28
  </div>
26
29
  </div>
30
+ <div
31
+ v-if="count"
32
+ class="relative -top-px ml-[5px]"
33
+ >
34
+ <BaseCounter
35
+ :count="count"
36
+ :max-digit="2"
37
+ :color="color"
38
+ :size="size"
39
+ />
40
+ </div>
27
41
  </template>
28
42
 
29
43
  <script lang="ts" setup>
@@ -36,6 +50,10 @@ const props = defineProps({
36
50
  default: undefined,
37
51
  type: String,
38
52
  },
53
+ description: {
54
+ default: undefined,
55
+ type: String,
56
+ },
39
57
  icon: {
40
58
  default: undefined,
41
59
  type: String,
@@ -134,7 +152,13 @@ const buttonStyles = computed((): StyleValue => {
134
152
  });
135
153
 
136
154
  const iconClasses = computed((): string => {
137
- let baseClasses = ' w-5 h-5 shrink-0 mr-2';
155
+ let baseClasses = ' w-5 h-5 shrink-0 ';
156
+
157
+ if (props.size == 'xs') {
158
+ baseClasses += ' mr-2 ';
159
+ } else {
160
+ baseClasses += ' mr-3 ';
161
+ }
138
162
 
139
163
  if (!props.active) {
140
164
  baseClasses += ' opacity-70 ' + textColor.value;
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <nav :class="classInternal">
3
- <BaseContainer :size="size">
3
+ <BaseContainer size="7xl">
4
4
  <div
5
5
  :style="{
6
- height: `${height}px`,
6
+ height: `${heightInner}px`,
7
7
  }"
8
8
  class="flex justify-between"
9
9
  >
@@ -12,6 +12,7 @@
12
12
  <slot
13
13
  name="navbar"
14
14
  :dark="dark"
15
+ :height="heightInner"
15
16
  />
16
17
  </div>
17
18
 
@@ -77,16 +78,12 @@ defineOptions({
77
78
  })
78
79
 
79
80
  const props = defineProps({
80
- size: {
81
- default: '7xl',
82
- type: String,
83
- },
84
81
  dark: {
85
82
  default: false,
86
83
  type: Boolean,
87
84
  },
88
85
  height: {
89
- default: 64,
86
+ default: undefined,
90
87
  type: Number,
91
88
  },
92
89
  breakpoint: {
@@ -96,7 +93,11 @@ const props = defineProps({
96
93
  class: {
97
94
  default: '',
98
95
  type: [String, Array] as PropType<string | string[]>,
99
- }
96
+ },
97
+ size: {
98
+ default: 'md',
99
+ type: String as PropType<'xs' | 'sm' | 'md'>,
100
+ },
100
101
  });
101
102
 
102
103
  const window = useWindowSize();
@@ -105,13 +106,29 @@ const mobile = computed(() => {
105
106
  return window.width.value < props.breakpoint;
106
107
  });
107
108
 
109
+ const heightInner = computed(() => {
110
+ if (props.height) {
111
+ return props.height;
112
+ }
113
+
114
+ if (props.size == 'xs') {
115
+ return 48;
116
+ }
117
+
118
+ if (props.size == 'sm') {
119
+ return 56;
120
+ }
121
+
122
+ return 64;
123
+ });
124
+
108
125
  const backgroundClass = computed(() => {
109
126
  return props.dark ? 'bg-slate-900' : 'bg-white';
110
127
  });
111
128
 
112
129
  const classInternal = computed(() => {
113
130
  return twMerge(
114
- 'w-full border-t-[3px] border-primary-500',
131
+ 'w-full',
115
132
  backgroundClass.value,
116
133
  props.class,
117
134
  )
@@ -134,4 +151,5 @@ function closeMenu() {
134
151
  provide('toggleMenu', toggleMenu);
135
152
  provide('openMenu', openMenu);
136
153
  provide('closeMenu', closeMenu);
154
+ provide('navbar:height', heightInner);
137
155
  </script>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="flex">
2
+ <div class="flex group relative">
3
3
  <BaseActionItem
4
4
  :item="item"
5
5
  class="flex w-full"
@@ -7,6 +7,7 @@
7
7
  >
8
8
  <template #default="{ active }">
9
9
  <BaseNavbarItemContent
10
+ :size="size"
10
11
  :label="item.label"
11
12
  :icon="item.icon"
12
13
  :active="active"
@@ -15,14 +16,41 @@
15
16
  />
16
17
  </template>
17
18
  </BaseActionItem>
19
+ <div
20
+ v-if="item.actions && item.actions.length"
21
+ class="absolute ring-1 ring-black ring-opacity-10 shadow-lg rounded-md invisible p-1 left-0 bg-white w-56 duration-100 opacity-0 translate-y-1 || group-hover:visible group-hover:opacity-100 group-hover:translate-y-0"
22
+ :style="{
23
+ top: `${height - 3}px`,
24
+ }"
25
+ >
26
+ <div class="space-y-px">
27
+ <div
28
+ v-for="subItem in item.actions"
29
+ :key="subItem.label"
30
+ >
31
+ <BaseActionItem
32
+ :item="subItem"
33
+ >
34
+ <BaseMenuItem
35
+ :size="size == 'xs' ? 'xs' : 'sm'"
36
+ :label="subItem.label"
37
+ :count="subItem.count"
38
+ :icon="subItem.icon"
39
+ :description="subItem.description"
40
+ />
41
+ </BaseActionItem>
42
+ </div>
43
+ </div>
44
+ </div>
18
45
  </div>
19
46
  </template>
20
47
 
21
48
  <script setup lang="ts">
22
- import { PropType } from 'vue';
49
+ import { PropType, Ref } from 'vue';
23
50
  import { ActionItem } from '@/types/ActionItem';
24
51
  import BaseActionItem from './BaseActionItem.vue';
25
52
  import BaseNavbarItemContent from './BaseNavbarItemContent.vue';
53
+ import BaseMenuItem from './BaseMenuItem.vue';
26
54
 
27
55
  defineProps({
28
56
  item: {
@@ -33,8 +61,14 @@ defineProps({
33
61
  default: false,
34
62
  type: Boolean,
35
63
  },
64
+ size: {
65
+ default: 'md',
66
+ type: String as PropType<'xs' | 'sm' | 'md'>,
67
+ },
36
68
  });
37
69
 
70
+ const height = inject('navbar:height') as Ref<number>;
71
+
38
72
  const closeMenu = inject('closeMenu') as () => void;
39
73
 
40
74
  const emit = defineEmits(['click']);
@@ -1,22 +1,24 @@
1
1
  <template>
2
2
  <div :class="classes">
3
- <div class="group flex grow items-center">
3
+ <div :class="classesInner">
4
4
  <BaseIcon
5
5
  v-if="icon"
6
6
  :icon="icon"
7
7
  :class="iconClasses"
8
8
  />
9
- <span :class="[sizeClasses]">{{ label }}</span>
10
- </div>
11
- <div
12
- v-if="count"
13
- class="relative -top-px ml-[5px]"
14
- >
15
- <BaseCounter
16
- :count="count"
17
- :max-digit="2"
18
- :color="'primary'"
19
- />
9
+ <div :class="[sizeClasses, 'text-base']">
10
+ {{ label }}
11
+ </div>
12
+ <div
13
+ v-if="count"
14
+ class="relative -top-px ml-[5px]"
15
+ >
16
+ <BaseCounter
17
+ :count="count"
18
+ :max-digit="2"
19
+ color="danger"
20
+ />
21
+ </div>
20
22
  </div>
21
23
  </div>
22
24
  </template>
@@ -49,28 +51,27 @@ const props = defineProps({
49
51
  },
50
52
  size: {
51
53
  default: 'md',
52
- type: String as PropType<'sm' | 'md'>,
54
+ type: String as PropType<'xs' | 'sm' | 'md'>,
53
55
  },
54
56
  });
55
57
 
56
58
  const classes = computed(() => {
57
59
  const classList = [
58
- 'text-left border-b-2 px-2 flex items-center text-base font-medium w-full',
60
+ 'flex items-center w-full',
59
61
  ];
60
62
 
61
63
  if (props.active) {
62
64
  if (props.dark) {
63
- classList.push('border-white text-white');
65
+ classList.push('text-white');
64
66
  } else {
65
- classList.push('border-primary-500 text-slate-900');
67
+ classList.push('text-slate-900');
66
68
  }
67
69
  } else {
68
- classList.push('border-transparent');
69
70
  if (props.dark) {
70
- classList.push('hover:border-white hover:text-white text-slate-200');
71
+ classList.push('hover:text-white text-slate-200');
71
72
  } else {
72
73
  classList.push(
73
- 'hover:border-primary-500 hover:text-slate-900 text-slate-800'
74
+ 'hover:text-slate-900 text-slate-800'
74
75
  );
75
76
  }
76
77
  }
@@ -78,6 +79,20 @@ const classes = computed(() => {
78
79
  return classList;
79
80
  });
80
81
 
82
+ const classesInner = computed(() => {
83
+ const classList = [
84
+ 'px-3 py-1 grow rounded-md duration-100 flex items-center font-medium',
85
+ ];
86
+
87
+ if (props.dark) {
88
+ classList.push('hover:bg-slate-700');
89
+ } else {
90
+ classList.push('hover:bg-slate-100');
91
+ }
92
+
93
+ return classList;
94
+ });
95
+
81
96
  const iconClasses = computed((): string[] => {
82
97
  const classList = ['w-5 h-5 shrink-0 mr-2 leading-none inline-block'];
83
98
  if (props.active) {
@@ -89,6 +104,9 @@ const iconClasses = computed((): string[] => {
89
104
  });
90
105
 
91
106
  const sizeClasses = computed(() => {
107
+ if (props.size == 'xs') {
108
+ return 'text-xs';
109
+ }
92
110
  if (props.size == 'sm') {
93
111
  return 'text-sm';
94
112
  }
@@ -16,7 +16,7 @@
16
16
  :size="size"
17
17
  :count="count"
18
18
  :max-digit="2"
19
- :color="'primary'"
19
+ color="danger"
20
20
  />
21
21
  </div>
22
22
  </div>
@@ -20,19 +20,14 @@
20
20
  >
21
21
  <div class="relative flex py-1">
22
22
  <div
23
- v-if="isActiveInternal(isActive, isExactActive)"
24
- class="absolute left-0 bottom-0 w-full h-[2px] bg-primary-600"
25
- />
26
- <div
27
- class="whitespace-nowrap rounded-md"
28
23
  :class="[
29
- isActiveInternal(isActive, isExactActive)
30
- ? 'group-hover:bg-primary-100'
31
- : 'group-hover:bg-slate-200',
24
+ 'whitespace-nowrap rounded-md group-hover:bg-black group-hover:bg-opacity-5',
32
25
  sizeClassInner
33
26
  ]"
34
27
  >
35
- <slot :active="isActiveInternal(isActive, isExactActive)" />
28
+ <div class="base-tab-item-slot">
29
+ <slot :active="isActiveInternal(isActive, isExactActive)" />
30
+ </div>
36
31
  </div>
37
32
  </div>
38
33
  </a>
@@ -30,8 +30,12 @@ const Template = (args) => ({
30
30
  <BaseTabItem to="/" v-slot="{active}">
31
31
  <div class="flex items-center">
32
32
  <span class="mr-1">Home</span>
33
- <BaseCounter :size="['lg', 'md'].includes(args.size) ? 'sm' : 'xs'" :color="active ? 'primary' : 'light'" :count="1"></BaseCounter>
34
- </div>
33
+ <BaseCounter
34
+ :size="['lg', 'md'].includes(args.size) ? 'sm' : 'xs'"
35
+ :color="active ? 'primary' : 'light'"
36
+ :count="1"
37
+ ></BaseCounter>
38
+ </div>
35
39
  </BaseTabItem>
36
40
  <BaseTabItem to="/setup">
37
41
  Setup