sprintify-ui 0.10.24 → 0.10.25

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.
@@ -1,32 +1,50 @@
1
+ import { ComputedRef } from 'vue';
1
2
  import { NavigationFailure, RouteLocationRaw } from 'vue-router';
2
3
  type __VLS_Props = {
3
- to: RouteLocationRaw;
4
+ id?: string;
5
+ to?: RouteLocationRaw;
4
6
  disabled?: boolean;
7
+ /**
8
+ * The strategy to determine if the tab is active. Only used when the
9
+ * `to` prop is provided.
10
+ * - `default`: The tab is active if the route matches the path.
11
+ * - `exact`: The tab is active if the route matches the path exactly.
12
+ */
5
13
  activeStrategy?: 'default' | 'exact';
6
14
  };
7
- declare function onClick(navigate: () => Promise<void | NavigationFailure>): Promise<void | NavigationFailure> | undefined;
15
+ declare const idInternal: ComputedRef<string>;
16
+ declare const currentTabId: ComputedRef<string | null>;
17
+ declare function onClick(navigate?: () => Promise<void | NavigationFailure> | null): Promise<void | NavigationFailure> | null | undefined;
8
18
  declare function isActiveInternal(isActive: boolean, isExactActive: boolean): boolean;
9
19
  declare const baseTabItemRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
10
- declare const sizeClassOuter: import("vue").ComputedRef<"" | "px-1" | "px-1.5" | "px-2">;
11
- declare const sizeClassInner: import("vue").ComputedRef<"" | "text-xs py-1 px-1 font-normal" | "text-sm py-1 px-2 font-normal" | "text-sm py-1.5 px-2 font-normal" | "text-base py-1.5 px-3 font-normal">;
20
+ declare const sizeClassOuter: ComputedRef<string>;
21
+ declare const sizeClassInner: ComputedRef<"" | "text-xs py-1 px-1 font-normal" | "text-sm py-1 px-2 font-normal" | "text-sm py-1.5 px-2 font-normal" | "text-base py-1.5 px-3 font-normal">;
12
22
  declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
13
23
  declare var __VLS_5: {
14
24
  active: boolean;
15
- };
25
+ }, __VLS_7: {};
16
26
  type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
17
27
  default?: (props: typeof __VLS_5) => any;
28
+ } & {
29
+ default?: (props: typeof __VLS_7) => any;
18
30
  }>;
19
31
  declare const __VLS_self: import("vue").DefineComponent<__VLS_Props, {
32
+ idInternal: typeof idInternal;
33
+ currentTabId: typeof currentTabId;
20
34
  onClick: typeof onClick;
21
35
  isActiveInternal: typeof isActiveInternal;
22
36
  baseTabItemRef: typeof baseTabItemRef;
23
37
  sizeClassOuter: typeof sizeClassOuter;
24
38
  sizeClassInner: typeof sizeClassInner;
25
39
  }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
40
+ to: RouteLocationRaw;
41
+ id: string;
26
42
  disabled: boolean;
27
43
  activeStrategy: "default" | "exact";
28
44
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
45
  declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
46
+ to: RouteLocationRaw;
47
+ id: string;
30
48
  disabled: boolean;
31
49
  activeStrategy: "default" | "exact";
32
50
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,4 +1,5 @@
1
1
  type __VLS_Props = {
2
+ modelValue?: string | number | null | undefined;
2
3
  size?: 'xs' | 'sm' | 'md' | 'lg';
3
4
  };
4
5
  declare const scrollable: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
@@ -11,11 +12,21 @@ type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$
11
12
  declare const __VLS_self: import("vue").DefineComponent<__VLS_Props, {
12
13
  scrollable: typeof scrollable;
13
14
  lineRef: typeof lineRef;
14
- }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
15
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
16
+ "update:modelValue": (...args: any[]) => void;
17
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
18
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
19
+ }>, {
15
20
  size: "xs" | "sm" | "md" | "lg";
21
+ modelValue: string | number | null;
16
22
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
- declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
23
+ declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
24
+ "update:modelValue": (...args: any[]) => void;
25
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
26
+ "onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
27
+ }>, {
18
28
  size: "xs" | "sm" | "md" | "lg";
29
+ modelValue: string | number | null;
19
30
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
31
  declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
21
32
  export default _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.10.24",
3
+ "version": "0.10.25",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && vue-tsc && vite build",
@@ -4,6 +4,7 @@
4
4
  class="[&:first-child_a]:pl-0 [&:last-child_a]:pr-0"
5
5
  >
6
6
  <RouterLink
7
+ v-if="to"
7
8
  v-slot="{ href, navigate, isActive, isExactActive }"
8
9
  :to="to"
9
10
  custom
@@ -16,7 +17,6 @@
16
17
  isActiveInternal(isActive, isExactActive)
17
18
  ? 'active text-primary-600'
18
19
  : 'text-slate-600 hover:text-slate-900',
19
- disabled ? 'cursor-not-allowed opacity-60' : '',
20
20
  sizeClassOuter,
21
21
  ]"
22
22
  @click.prevent="onClick(navigate)"
@@ -35,40 +35,104 @@
35
35
  </div>
36
36
  </a>
37
37
  </RouterLink>
38
+ <button
39
+ v-else
40
+ type="button"
41
+ :disabled="disabled"
42
+ class="group inline-block rounded-t-lg"
43
+ :class="[
44
+ currentTabId === idInternal
45
+ ? 'active text-primary-600'
46
+ : 'text-slate-600 hover:text-slate-900',
47
+ sizeClassOuter,
48
+ ]"
49
+ @click="onClick()"
50
+ >
51
+ <div class="relative flex py-1">
52
+ <div
53
+ :class="[
54
+ 'whitespace-nowrap rounded-md group-hover:bg-black group-hover:bg-opacity-5',
55
+ sizeClassInner
56
+ ]"
57
+ >
58
+ <div class="base-tab-item-slot">
59
+ <slot />
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </button>
38
64
  </li>
39
65
  </template>
40
66
 
41
67
  <script lang="ts" setup>
42
68
  import { useElementBounding } from '@vueuse/core';
69
+ import { uniqueId } from 'lodash';
70
+ import { ComputedRef } from 'vue';
43
71
  import { NavigationFailure, RouteLocationRaw } from 'vue-router';
44
72
 
73
+ const router = useRouter();
74
+
45
75
  const props = withDefaults(
46
76
  defineProps<{
47
- to: RouteLocationRaw;
77
+ id?: string;
78
+ to?: RouteLocationRaw;
48
79
  disabled?: boolean;
80
+ /**
81
+ * The strategy to determine if the tab is active. Only used when the
82
+ * `to` prop is provided.
83
+ * - `default`: The tab is active if the route matches the path.
84
+ * - `exact`: The tab is active if the route matches the path exactly.
85
+ */
49
86
  activeStrategy?: 'default' | 'exact';
50
87
  }>(),
51
88
  {
89
+ id: undefined,
90
+ to: undefined,
52
91
  disabled: false,
53
92
  activeStrategy: 'default',
54
93
  }
55
94
  );
56
95
 
96
+ const idInternal = computed(() => {
97
+ if (props.id) {
98
+ return props.id;
99
+ }
100
+
101
+ if (!router || !props.to) {
102
+ return uniqueId() + '';
103
+ }
104
+
105
+ const route = router.resolve(props.to);
106
+ const path = route.fullPath;
107
+
108
+ return path;
109
+ });
110
+
111
+ const currentTabId = inject<ComputedRef<string | null>>('tabs:currentTabId', computed(() => null));
112
+ const updateTabId = inject<((tabId: string) => void)>('tabs:updateTabId');
113
+
57
114
  const size = inject('tabs:size', ref<'xs' | 'sm' | 'md' | 'lg'>('md'));
58
- const animate = inject('tabs:animate', () => { });
59
115
 
60
- function onClick(navigate: () => Promise<void | NavigationFailure>) {
116
+ function onClick(navigate?: () => Promise<void | NavigationFailure> | null) {
61
117
  if (props.disabled) {
62
118
  return;
63
119
  }
64
120
 
65
- return navigate();
121
+ if (updateTabId) {
122
+ updateTabId(idInternal.value);
123
+ }
124
+
125
+ if (navigate) {
126
+ return navigate();
127
+ }
66
128
  }
67
129
 
68
130
  function isActiveInternal(isActive: boolean, isExactActive: boolean) {
69
131
  return props.activeStrategy == 'default' ? isActive : isExactActive;
70
132
  }
71
133
 
134
+ // Animate
135
+
72
136
  const baseTabItemRef = ref<HTMLElement | null>(null);
73
137
 
74
138
  const { x, y, width } = useElementBounding(baseTabItemRef);
@@ -80,20 +144,35 @@ watch(
80
144
  }
81
145
  );
82
146
 
147
+ const animate = inject('tabs:animate', () => { });
148
+
149
+ // Classes
83
150
 
84
151
  const sizeClassOuter = computed(() => {
152
+
153
+ const classes = [];
154
+
155
+ if (props.disabled) {
156
+ classes.push('cursor-not-allowed opacity-60');
157
+ }
158
+
85
159
  switch (size.value) {
86
160
  case 'xs':
87
- return 'px-1';
161
+ classes.push('px-1');
162
+ break;
88
163
  case 'sm':
89
- return 'px-1.5';
164
+ classes.push('px-1.5');
165
+ break;
90
166
  case 'md':
91
- return 'px-2';
167
+ classes.push('px-2');
168
+ break;
92
169
  case 'lg':
93
- return 'px-2';
94
- default:
95
- return '';
170
+ classes.push('px-3');
171
+ break;
96
172
  }
173
+
174
+ return classes.join(' ');
175
+
97
176
  });
98
177
 
99
178
  const sizeClassInner = computed(() => {
@@ -23,16 +23,18 @@ const Template = (args) => ({
23
23
  setup() {
24
24
  const label = ref("Home");
25
25
 
26
+ const modelValue = ref("setup");
27
+
26
28
  setInterval(() => {
27
29
  label.value = Math.random().toString(36).substring(7);
28
30
  }, 1000);
29
31
 
30
- return { args, label };
32
+ return { args, label, modelValue };
31
33
  },
32
34
  template: `
33
35
  <div class="bg-slate-100 py-10">
34
36
  <BaseContainer>
35
- <BaseTabs v-bind="args">
37
+ <BaseTabs v-model="modelValue" v-bind="args">
36
38
  <BaseTabItem to="/" v-slot="{active}">
37
39
  <div class="flex items-center">
38
40
  <span class="mr-1">{{ label }}</span>
@@ -62,7 +64,9 @@ const Template = (args) => ({
62
64
  <div class="mt-10">
63
65
  <BaseCard>
64
66
  <BaseCardRow>
65
- {{ $route.path }}
67
+ path: {{ $route.path }}
68
+ <br>
69
+ id: {{ modelValue }}
66
70
  </BaseCardRow>
67
71
  </BaseCard>
68
72
  </div>
@@ -93,3 +97,70 @@ export const SizeLG = Template.bind({});
93
97
  SizeLG.args = {
94
98
  size: "lg",
95
99
  };
100
+
101
+ const TemplateStatic = (args) => ({
102
+ components: {
103
+ BaseTabs,
104
+ BaseTabItem,
105
+ BaseContainer,
106
+ BaseCard,
107
+ BaseCardRow,
108
+ BaseCounter,
109
+ },
110
+ setup() {
111
+ const label = ref("Home");
112
+
113
+ const modelValue = ref("setup");
114
+
115
+ setInterval(() => {
116
+ label.value = Math.random().toString(36).substring(7);
117
+ }, 1000);
118
+
119
+ return { args, label, modelValue };
120
+ },
121
+ template: `
122
+ <div class="bg-slate-100 py-10">
123
+ <BaseContainer>
124
+ <BaseTabs v-model="modelValue" v-bind="args">
125
+ <BaseTabItem id="home" v-slot="{active}">
126
+ <div class="flex items-center">
127
+ <span class="mr-1">{{ label }}</span>
128
+ <BaseCounter
129
+ :size="['lg', 'md'].includes(args.size) ? 'sm' : 'xs'"
130
+ :color="active ? 'primary' : 'light'"
131
+ :count="1"
132
+ ></BaseCounter>
133
+ </div>
134
+ </BaseTabItem>
135
+ <BaseTabItem id="setup">
136
+ Setup
137
+ </BaseTabItem>
138
+ <BaseTabItem id="settings">
139
+ Settings
140
+ </BaseTabItem>
141
+ <BaseTabItem id="articles">
142
+ Articles
143
+ </BaseTabItem>
144
+ <BaseTabItem id="misc">
145
+ Miscellaneous
146
+ </BaseTabItem>
147
+ <BaseTabItem id="users">
148
+ Users
149
+ </BaseTabItem>
150
+ </BaseTabs>
151
+
152
+ <div class="mt-10">
153
+ <BaseCard>
154
+ <BaseCardRow>
155
+ id: {{ modelValue }}
156
+ </BaseCardRow>
157
+ </BaseCard>
158
+ </div>
159
+ </BaseContainer>
160
+ </div>
161
+ `,
162
+ });
163
+
164
+ export const Static = TemplateStatic.bind({});
165
+ Static.args = {
166
+ };
@@ -29,23 +29,55 @@ import { debounce } from 'lodash';
29
29
 
30
30
  const props = withDefaults(
31
31
  defineProps<{
32
+ modelValue?: string | number | null | undefined;
32
33
  size?: 'xs' | 'sm' | 'md' | 'lg';
33
34
  }>(),
34
35
  {
36
+ modelValue: null,
35
37
  size: 'md',
36
38
  }
37
39
  );
38
40
 
39
41
  const route = useRoute();
42
+
40
43
  const scrollable = ref<HTMLElement | null>(null);
41
44
 
45
+ const emit = defineEmits(['update:modelValue']);
46
+
42
47
  watch(
43
48
  () => route.fullPath,
44
49
  () => {
45
50
  nextTick(() => {
46
- scrollToCenter();
47
- animateLine();
51
+ updateTab();
52
+ });
53
+ },
54
+ { immediate: true }
55
+ );
56
+
57
+ watch(
58
+ () => props.modelValue,
59
+ () => {
60
+ nextTick(() => {
61
+ updateTab();
48
62
  });
63
+ },
64
+ { immediate: true }
65
+ );
66
+
67
+ function updateTab() {
68
+ scrollToCenter();
69
+ animateLine();
70
+ }
71
+
72
+ provide(
73
+ 'tabs:currentTabId',
74
+ computed(() => props.modelValue)
75
+ );
76
+
77
+ provide(
78
+ 'tabs:updateTabId',
79
+ (tabId: string) => {
80
+ emit('update:modelValue', tabId);
49
81
  }
50
82
  );
51
83