@saasmakers/ui 2.0.27 → 2.0.29

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.
@@ -6,6 +6,7 @@ const props = withDefaults(defineProps<BaseEmoji>(), {
6
6
  clickable: true,
7
7
  emoji: undefined,
8
8
  hasBox: false,
9
+ legend: undefined,
9
10
  size: 'base',
10
11
  })
11
12
 
@@ -13,10 +14,6 @@ const emit = defineEmits<{
13
14
  click: [event: MouseEvent, emoji?: string]
14
15
  }>()
15
16
 
16
- defineSlots<{
17
- default?: () => VNode[]
18
- }>()
19
-
20
17
  const { urls } = useAppConfig()
21
18
 
22
19
  function onClick(event: MouseEvent) {
@@ -34,6 +31,7 @@ function onKeyDown(event: KeyboardEvent) {
34
31
  <span
35
32
  class="flex items-center justify-center transition"
36
33
  :class="{
34
+ 'relative': legend,
37
35
  'border base-border': hasBox,
38
36
  'bg-gray-100 dark:bg-gray-900 shadow-inner': hasBox && background === 'gray',
39
37
  'bg-white dark:bg-gray-900 shadow-sm': hasBox && background === 'white',
@@ -79,6 +77,11 @@ function onKeyDown(event: KeyboardEvent) {
79
77
  :src="`${urls.storage}/images/emojis/${emoji}.svg`"
80
78
  >
81
79
 
82
- <slot />
80
+ <BaseText
81
+ v-if="legend"
82
+ class="absolute bottom-0 left-0 right-0 select-none rounded-b bg-gray-200 text-center text-gray-900 leading-[0.75rem] opacity-90 -mx-px -mb-px dark:bg-gray-800 dark:text-gray-100"
83
+ size="3xs"
84
+ :text="legend"
85
+ />
83
86
  </span>
84
87
  </template>
@@ -0,0 +1,56 @@
1
+ <script lang="ts" setup>
2
+ import type { LayoutBackdrop } from '../../types/layout'
3
+
4
+ defineOptions({ inheritAttrs: false })
5
+
6
+ const props = withDefaults(defineProps<LayoutBackdrop>(), {
7
+ dragTransition: false,
8
+ opacity: undefined,
9
+ transitionless: false,
10
+ zIndex: 60,
11
+ })
12
+
13
+ const emit = defineEmits<{
14
+ click: [event: MouseEvent]
15
+ }>()
16
+
17
+ const visible = defineModel<boolean>({ default: false })
18
+
19
+ const backdropStyle = computed(() => {
20
+ if (props.opacity === undefined) {
21
+ return { zIndex: props.zIndex }
22
+ }
23
+
24
+ return {
25
+ opacity: props.opacity,
26
+ zIndex: props.zIndex,
27
+ }
28
+ })
29
+ </script>
30
+
31
+ <template>
32
+ <ClientOnly>
33
+ <Teleport to="body">
34
+ <Transition
35
+ enter-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
36
+ enter-from-class="opacity-0 motion-reduce:opacity-100"
37
+ enter-to-class="opacity-100"
38
+ leave-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
39
+ leave-from-class="opacity-100"
40
+ leave-to-class="opacity-0 motion-reduce:opacity-100"
41
+ >
42
+ <div
43
+ v-if="visible"
44
+ aria-hidden="true"
45
+ class="layout-backdrop fixed inset-0 bg-black/50"
46
+ :class="{
47
+ 'transition-none motion-reduce:transition-none': transitionless,
48
+ 'transition-opacity duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': dragTransition,
49
+ }"
50
+ :style="backdropStyle"
51
+ @click="emit('click', $event)"
52
+ />
53
+ </Transition>
54
+ </Teleport>
55
+ </ClientOnly>
56
+ </template>
@@ -177,31 +177,16 @@ watch(visible, (isVisible) => {
177
177
  </script>
178
178
 
179
179
  <template>
180
+ <LayoutBackdrop
181
+ v-model="visible"
182
+ :drag-transition="!drag.isDragging && (drag.closing || drag.offset > 0)"
183
+ :opacity="drag.closing ? 0 : drag.offset > 0 ? Math.max(0, 1 - drag.offset / (panelRef?.offsetHeight ?? 320)) : undefined"
184
+ :transitionless="drag.isDragging"
185
+ @click="onClose"
186
+ />
187
+
180
188
  <ClientOnly>
181
189
  <Teleport to="body">
182
- <Transition
183
- enter-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
184
- enter-from-class="opacity-0 motion-reduce:opacity-100"
185
- enter-to-class="opacity-100"
186
- leave-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
187
- leave-from-class="opacity-100"
188
- leave-to-class="opacity-0 motion-reduce:opacity-100"
189
- >
190
- <div
191
- v-if="visible"
192
- aria-hidden="true"
193
- class="layout-bottom-sheet fixed inset-0 z-[60] bg-black/50"
194
- :class="{
195
- 'transition-none motion-reduce:transition-none': drag.isDragging,
196
- 'transition-opacity duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging && (drag.closing || drag.offset > 0),
197
- }"
198
- :style="{
199
- opacity: drag.closing ? 0 : drag.offset > 0 ? Math.max(0, 1 - drag.offset / (panelRef?.offsetHeight ?? 320)) : undefined,
200
- }"
201
- @click="onClose"
202
- />
203
- </Transition>
204
-
205
190
  <Transition
206
191
  enter-active-class="transition-transform duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
207
192
  enter-from-class="translate-y-full motion-reduce:translate-y-0"
@@ -17,24 +17,13 @@ function onClose() {
17
17
  </script>
18
18
 
19
19
  <template>
20
+ <LayoutBackdrop
21
+ v-model="visible"
22
+ @click="onClose"
23
+ />
24
+
20
25
  <ClientOnly>
21
26
  <Teleport to="body">
22
- <Transition
23
- enter-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
24
- enter-from-class="opacity-0 motion-reduce:opacity-100"
25
- enter-to-class="opacity-100"
26
- leave-active-class="transition-opacity duration-300 ease motion-reduce:transition-none motion-reduce:duration-0"
27
- leave-from-class="opacity-100"
28
- leave-to-class="opacity-0 motion-reduce:opacity-100"
29
- >
30
- <div
31
- v-if="visible"
32
- aria-hidden="true"
33
- class="layout-modal fixed inset-0 z-[60] bg-black/50"
34
- @click="onClose"
35
- />
36
- </Transition>
37
-
38
27
  <Transition
39
28
  enter-active-class="transition duration-200 ease motion-reduce:transition-none motion-reduce:duration-0"
40
29
  enter-from-class="opacity-0 scale-95 motion-reduce:scale-100 motion-reduce:opacity-100"
@@ -47,7 +47,7 @@ export default function useToast() {
47
47
  const toast: BaseToast = {
48
48
  action: params.action,
49
49
  id: crypto.randomUUID(),
50
- status: params.status,
50
+ status: params.status ?? 'success',
51
51
  text,
52
52
  }
53
53
 
@@ -169,6 +169,7 @@ export interface BaseEmoji {
169
169
  clickable?: boolean
170
170
  emoji?: string
171
171
  hasBox?: boolean
172
+ legend?: string
172
173
  size?: BaseSize
173
174
  }
174
175
 
@@ -230,25 +231,6 @@ export type BaseMetricPerformance = 'down' | 'equal' | 'up'
230
231
 
231
232
  export type BaseMetricSize = 'base' | 'sm'
232
233
 
233
- export interface BaseOverlay {
234
- active?: boolean
235
- clickable?: boolean
236
- fixed?: boolean
237
- hasClose?: boolean
238
- opacity?: BaseOverlayOpacity
239
- position?: BaseOverlayPosition
240
- }
241
-
242
- export type BaseOverlayOpacity
243
- = | 0
244
- | 25
245
- | 50
246
- | 75
247
- | 95
248
- | 100
249
-
250
- export type BaseOverlayPosition = 'absolute' | 'fixed'
251
-
252
234
  export interface BaseParagraph {
253
235
  alignment?: BaseAlignment
254
236
  size?: BaseParagraphSize
@@ -55,9 +55,6 @@ declare global {
55
55
  type BaseMetricAlignment = import('./bases').BaseMetricAlignment
56
56
  type BaseMetricPerformance = import('./bases').BaseMetricPerformance
57
57
  type BaseMetricSize = import('./bases').BaseMetricSize
58
- type BaseOverlay = import('./bases').BaseOverlay
59
- type BaseOverlayOpacity = import('./bases').BaseOverlayOpacity
60
- type BaseOverlayPosition = import('./bases').BaseOverlayPosition
61
58
  type BaseParagraph = import('./bases').BaseParagraph
62
59
  type BaseParagraphAlignment = import('./bases').BaseParagraphAlignment
63
60
  type BaseParagraphSize = import('./bases').BaseParagraphSize
@@ -117,6 +114,7 @@ declare global {
117
114
 
118
115
  // Layout
119
116
  type LayoutApps = import('./layout').LayoutApps
117
+ type LayoutBackdrop = import('./layout').LayoutBackdrop
120
118
  type LayoutBottomSheet = import('./layout').LayoutBottomSheet
121
119
  type LayoutModal = import('./layout').LayoutModal
122
120
 
@@ -3,6 +3,13 @@ export interface LayoutApps {
3
3
  playStoreId?: string
4
4
  }
5
5
 
6
+ export interface LayoutBackdrop {
7
+ dragTransition?: boolean
8
+ opacity?: number
9
+ transitionless?: boolean
10
+ zIndex?: number
11
+ }
12
+
6
13
  export interface LayoutBottomSheet {
7
14
  title?: string
8
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.0.27",
3
+ "version": "2.0.29",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",
@@ -60,8 +60,8 @@
60
60
  "devDependencies": {
61
61
  "nuxt": "4.3.1",
62
62
  "typescript": "5.9.3",
63
- "@saasmakers/apps-helpers": "0.1.0",
64
- "@saasmakers/shared": "0.2.8"
63
+ "@saasmakers/shared": "0.2.8",
64
+ "@saasmakers/apps-helpers": "0.1.0"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@saasmakers/apps-helpers": "^0.1.0",
@@ -1,97 +0,0 @@
1
- <script lang="ts" setup>
2
- import { onKeyStroke } from '@vueuse/core'
3
- import { Motion } from 'motion-v'
4
- import type { BaseOverlay } from '../../types/bases'
5
-
6
- const props = withDefaults(defineProps<BaseOverlay>(), {
7
- active: true,
8
- clickable: true,
9
- fixed: true,
10
- hasClose: true,
11
- opacity: 75,
12
- position: 'absolute',
13
- })
14
-
15
- const emit = defineEmits<{
16
- click: [event: MouseEvent]
17
- close: [event: KeyboardEvent | MouseEvent]
18
- }>()
19
-
20
- defineSlots<{
21
- default?: () => VNode[]
22
- }>()
23
-
24
- const isClickable = computed(() => {
25
- return props.clickable || props.hasClose
26
- })
27
-
28
- function onClick(event: MouseEvent) {
29
- emit('click', event)
30
- }
31
-
32
- function onClose(event: KeyboardEvent | MouseEvent) {
33
- emit('close', event)
34
- }
35
-
36
- function onKeyDown(event: KeyboardEvent) {
37
- if (isClickable.value) {
38
- (event.currentTarget as HTMLElement).click()
39
- }
40
- }
41
-
42
- onKeyStroke('Escape', (event) => {
43
- event.preventDefault()
44
- onClose(event)
45
- })
46
- </script>
47
-
48
- <template>
49
- <div
50
- :class="{
51
- 'fixed inset-0': fixed,
52
- 'cursor-pointer': isClickable,
53
- }"
54
- role="button"
55
- tabindex="0"
56
- @click="onClick"
57
- @keydown.enter="onKeyDown"
58
- @keydown.space.prevent="onKeyDown"
59
- >
60
- <BaseIcon
61
- v-if="hasClose"
62
- class="pointer-events-auto absolute right-4 top-4 z-50 text-gray-200 dark:text-gray-800"
63
- clickable
64
- :icon="getLayerIcon('close')"
65
- @click="onClose"
66
- />
67
-
68
- <div
69
- v-if="$slots.default"
70
- class="relative z-50 h-full w-full"
71
- >
72
- <slot />
73
- </div>
74
-
75
- <Motion
76
- :animate="fadeIn.animate"
77
- as="span"
78
- class="absolute inset-0 z-40"
79
- :initial="fadeIn.initial"
80
- >
81
- <div
82
- class="inset-0 bg-gray-900 dark:bg-gray-100"
83
- :class="{
84
- 'h-0 w-0': !active,
85
- 'opacity-0': opacity === 0,
86
- 'opacity-25': opacity === 25,
87
- 'opacity-50': opacity === 50,
88
- 'opacity-75': opacity === 75,
89
- 'opacity-95': opacity === 95,
90
- 'opacity-100': opacity === 100,
91
- 'absolute': position === 'absolute',
92
- 'fixed': position === 'fixed',
93
- }"
94
- />
95
- </Motion>
96
- </div>
97
- </template>