vueless 1.3.5-beta.2 → 1.3.5-beta.3

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,7 +1,7 @@
1
1
  import { onMounted, ref, watch, computed, onBeforeUnmount } from "vue";
2
2
  import { isSSR } from "../utils/helper";
3
3
 
4
- import type { Ref } from "vue";
4
+ type ResponsiveConfig<T> = Partial<Record<BreakpointName, T>>;
5
5
 
6
6
  enum BreakpointName {
7
7
  Xs = "xs",
@@ -21,12 +21,16 @@ enum BreakpointWidth {
21
21
  "2xl" = 1536,
22
22
  }
23
23
 
24
- export function useBreakpoint() {
25
- let animationId: number | undefined;
24
+ let isInitialized = false;
25
+ let animationId: number | undefined;
26
+
27
+ const windowWidth = ref(0);
28
+ const currentBreakpoint = ref(BreakpointName.Xs);
29
+ const BREAKPOINT_KEYS = Object.keys(BreakpointName) as (keyof typeof BreakpointName)[];
26
30
 
27
- const windowWidth = ref(0);
28
- const currentBreakpoint: Ref<BreakpointName> = ref(BreakpointName.Xs);
31
+ watch(windowWidth, setBreakpoint, { immediate: true });
29
32
 
33
+ export function useBreakpoint() {
30
34
  const isPhone = computed(() => {
31
35
  return currentBreakpoint.value === BreakpointName.Xs;
32
36
  });
@@ -63,50 +67,14 @@ export function useBreakpoint() {
63
67
  return isDesktop.value || isLargeDesktop.value;
64
68
  });
65
69
 
66
- watch(windowWidth, setBreakpoint, { immediate: true });
67
-
68
70
  onMounted(() => {
69
- if (isSSR) return;
70
-
71
- windowWidth.value = window.innerWidth;
72
-
73
- window.addEventListener("resize", resizeListener, { passive: true });
71
+ initBreakpointListener();
74
72
  });
75
73
 
76
74
  onBeforeUnmount(() => {
77
- if (isSSR) return;
78
-
79
75
  window.removeEventListener("resize", resizeListener);
80
76
  });
81
77
 
82
- function resizeListener() {
83
- if (isSSR) return;
84
-
85
- if (animationId) {
86
- window.cancelAnimationFrame(animationId);
87
- }
88
-
89
- animationId = window.requestAnimationFrame(() => {
90
- windowWidth.value = window.innerWidth;
91
- });
92
- }
93
-
94
- function setBreakpoint(newWindowWidth: number) {
95
- if (newWindowWidth === undefined) return;
96
-
97
- const breakpoints = [
98
- { width: BreakpointWidth["2xl"], name: BreakpointName["2xl"] },
99
- { width: BreakpointWidth.Xl, name: BreakpointName.Xl },
100
- { width: BreakpointWidth.Lg, name: BreakpointName.Lg },
101
- { width: BreakpointWidth.Md, name: BreakpointName.Md },
102
- { width: BreakpointWidth.Sm, name: BreakpointName.Sm },
103
- ];
104
-
105
- currentBreakpoint.value =
106
- breakpoints.find((breakpoint) => newWindowWidth >= breakpoint.width)?.name ||
107
- BreakpointName.Xs;
108
- }
109
-
110
78
  return {
111
79
  isPhone,
112
80
  isLargePhone,
@@ -120,3 +88,90 @@ export function useBreakpoint() {
120
88
  breakpoint: currentBreakpoint,
121
89
  };
122
90
  }
91
+
92
+ /**
93
+ * Shorthand function that can be used directly in templates.
94
+ * Returns the appropriate value based on the current breakpoint.
95
+ * Vue will track the reactive dependency and re-render when the breakpoint changes.
96
+ *
97
+ * @example
98
+ * ```vue
99
+ * <template>
100
+ * <UButton :size="r({ sm: 'sm', md: 'md' })">Click me</UButton>
101
+ * </template>
102
+ *
103
+ * <script setup>
104
+ * import { r } from "vueless";
105
+ * </script>
106
+ * ```
107
+ */
108
+ export function r<T>(config: ResponsiveConfig<T>): T {
109
+ initBreakpointListener();
110
+
111
+ const definedKeys = BREAKPOINT_KEYS.filter((key) => BreakpointName[key] in config);
112
+
113
+ if (!definedKeys.length) {
114
+ return undefined as T;
115
+ }
116
+
117
+ const currentIndex = BREAKPOINT_KEYS.findIndex(
118
+ (key) => BreakpointName[key] === currentBreakpoint.value,
119
+ );
120
+ const smallestDefinedIndex = BREAKPOINT_KEYS.indexOf(definedKeys[0]);
121
+ const largestDefinedIndex = BREAKPOINT_KEYS.indexOf(definedKeys[definedKeys.length - 1]);
122
+
123
+ if (currentIndex <= smallestDefinedIndex) {
124
+ return config[BreakpointName[definedKeys[0]]] as T;
125
+ }
126
+
127
+ if (currentIndex >= largestDefinedIndex) {
128
+ return config[BreakpointName[definedKeys[definedKeys.length - 1]]] as T;
129
+ }
130
+
131
+ for (let i = currentIndex; i >= 0; i--) {
132
+ const bp = BreakpointName[BREAKPOINT_KEYS[i]];
133
+
134
+ if (bp in config) {
135
+ return config[bp] as T;
136
+ }
137
+ }
138
+
139
+ return config[BreakpointName[definedKeys[0]]] as T;
140
+ }
141
+
142
+ function setBreakpoint(newWindowWidth: number) {
143
+ if (newWindowWidth === undefined) return;
144
+
145
+ for (let i = BREAKPOINT_KEYS.length - 1; i >= 0; i--) {
146
+ const key = BREAKPOINT_KEYS[i];
147
+
148
+ if (newWindowWidth >= BreakpointWidth[key]) {
149
+ currentBreakpoint.value = BreakpointName[key];
150
+
151
+ return;
152
+ }
153
+ }
154
+
155
+ currentBreakpoint.value = BreakpointName.Xs;
156
+ }
157
+
158
+ function resizeListener() {
159
+ if (isSSR) return;
160
+
161
+ if (animationId) {
162
+ window.cancelAnimationFrame(animationId);
163
+ }
164
+
165
+ animationId = window.requestAnimationFrame(() => {
166
+ windowWidth.value = window.innerWidth;
167
+ });
168
+ }
169
+
170
+ function initBreakpointListener() {
171
+ if (isInitialized || isSSR) return;
172
+
173
+ isInitialized = true;
174
+ windowWidth.value = window.innerWidth;
175
+
176
+ window.addEventListener("resize", resizeListener, { passive: true });
177
+ }
package/index.d.ts CHANGED
@@ -29,7 +29,7 @@ export { useLocale } from "./composables/useLocale";
29
29
  export { useUI } from "./composables/useUI";
30
30
  export { useDarkMode } from "./composables/useDarkMode";
31
31
  export { useRequestQueue } from "./composables/useRequestQueue";
32
- export { useBreakpoint } from "./composables/useBreakpoint";
32
+ export { useBreakpoint, r } from "./composables/useBreakpoint";
33
33
  export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
34
34
  export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
35
35
  export { useMutationObserver } from "./composables/useMutationObserver";
package/index.ts CHANGED
@@ -35,7 +35,7 @@ export { useLocale } from "./composables/useLocale";
35
35
  export { useUI } from "./composables/useUI";
36
36
  export { useDarkMode } from "./composables/useDarkMode";
37
37
  export { useRequestQueue } from "./composables/useRequestQueue";
38
- export { useBreakpoint } from "./composables/useBreakpoint";
38
+ export { useBreakpoint, r } from "./composables/useBreakpoint";
39
39
  export { useLoaderOverlay } from "./ui.loader-overlay/useLoaderOverlay";
40
40
  export { useLoaderProgress } from "./ui.loader-progress/useLoaderProgress";
41
41
  export { useMutationObserver } from "./composables/useMutationObserver";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vueless",
3
- "version": "1.3.5-beta.2",
3
+ "version": "1.3.5-beta.3",
4
4
  "description": "Vue Styleless UI Component Library, powered by Tailwind CSS.",
5
5
  "author": "Johnny Grid <hello@vueless.com> (https://vueless.com)",
6
6
  "homepage": "https://vueless.com",