@saasmakers/ui 1.4.56 → 1.5.1

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.
@@ -55,7 +55,7 @@ function onClick(event: MouseEvent) {
55
55
  <template>
56
56
  <component
57
57
  :is="to ? NuxtLinkLocale : 'button'"
58
- class="relative inline-block select-none appearance-none border focus-visible:outline-none"
58
+ class="relative inline-block select-none appearance-none border uppercase focus-visible:outline-none"
59
59
  :class="{
60
60
  'flex items-center justify-center': circular,
61
61
  'cursor-not-allowed opacity-50': disabled,
@@ -64,6 +64,7 @@ function onClick(event: MouseEvent) {
64
64
  'border border-gray-200 dark:border-gray-800 bg-white dark:bg-gray-900 shadow-sm p-4': hasBackground && size === 'lg' && orientation === 'vertical',
65
65
  'hover:border-gray-300 dark:hover:border-gray-700': hasBackground && isClickable,
66
66
  'rounded-xl': hasBackground,
67
+ 'relative': orientation === 'vertical',
67
68
  }"
68
69
  :to="disabled ? undefined : to"
69
70
  @click="onClick"
@@ -83,8 +84,7 @@ function onClick(event: MouseEvent) {
83
84
  class="relative z-10 flex items-center justify-center flex-initial"
84
85
  :class="{
85
86
  'border shadow-inner': !avatar && !image,
86
- 'rounded-lg': !avatar && !image && orientation === 'horizontal',
87
- 'rounded-full': !avatar && !image && orientation === 'vertical',
87
+ 'rounded-lg': !avatar && !image,
88
88
  'border-gray-200 dark:border-gray-800 bg-gray-100 dark:bg-gray-900': !avatar && !image,
89
89
  'mr-2': orientation === 'horizontal' && size === 'base',
90
90
  'mr-3': orientation === 'horizontal' && size === 'lg',
@@ -100,10 +100,8 @@ function onClick(event: MouseEvent) {
100
100
 
101
101
  <BaseAvatar
102
102
  v-if="avatar"
103
- class="flex-initial"
103
+ class="rounded-lg flex-initial"
104
104
  :class="{
105
- 'rounded-lg': orientation === 'horizontal',
106
- 'rounded-full': orientation === 'vertical',
107
105
  'h-10 w-10': size === 'base' && orientation === 'horizontal',
108
106
  'h-12 w-12': size === 'lg' && orientation === 'horizontal',
109
107
  'h-20 w-20': size === 'base' && orientation === 'vertical',
@@ -132,10 +130,8 @@ function onClick(event: MouseEvent) {
132
130
 
133
131
  <img
134
132
  v-else-if="image"
135
- class="select-none object-cover shadow-sm transition-[filter] duration-300 drag-none flex-initial"
133
+ class="select-none rounded-lg object-cover shadow-sm transition-[filter] duration-300 drag-none flex-initial"
136
134
  :class="{
137
- 'rounded-lg': orientation === 'horizontal',
138
- 'rounded-full': orientation === 'vertical',
139
135
  'h-9 w-9': size === 'base' && orientation === 'horizontal',
140
136
  'h-11 w-11': size === 'lg' && orientation === 'horizontal',
141
137
  'h-20 w-20': size === 'base' && orientation === 'vertical',
@@ -194,8 +190,11 @@ function onClick(event: MouseEvent) {
194
190
 
195
191
  <BaseIcon
196
192
  v-if="isSelected"
197
- class="mr-1.5 self-center flex-initial"
193
+ class="flex-initial"
198
194
  :class="{
195
+ 'mr-1.5 self-center': orientation === 'horizontal',
196
+ 'absolute right-2 top-2': orientation === 'vertical' && size === 'base',
197
+ 'absolute right-2.5 top-2.5': orientation === 'vertical' && size === 'lg',
199
198
  'text-xl': size === 'base',
200
199
  'text-2xl': size === 'lg',
201
200
  }"
@@ -3,8 +3,10 @@ import type { BaseDivider } from '../../types/bases'
3
3
 
4
4
  const props = withDefaults(defineProps<BaseDivider>(), {
5
5
  borderStyle: 'solid',
6
+ color: 'black',
6
7
  hideNext: false,
7
8
  hidePrevious: false,
9
+ light: false,
8
10
  loading: false,
9
11
  navigable: false,
10
12
  pill: false,
@@ -12,6 +14,34 @@ const props = withDefaults(defineProps<BaseDivider>(), {
12
14
  title: '',
13
15
  })
14
16
 
17
+ const pillLineBorderClasses = computed(() => {
18
+ if (!props.pill) {
19
+ return 'border-gray-300 dark:border-gray-700'
20
+ }
21
+
22
+ if (props.color === 'indigo' && !props.light) {
23
+ return 'border-indigo-700 dark:border-indigo-300'
24
+ }
25
+
26
+ if (props.color === 'gray' && props.light) {
27
+ return 'border-gray-300 dark:border-gray-700'
28
+ }
29
+
30
+ return 'border-gray-900 dark:border-gray-300'
31
+ })
32
+
33
+ const pillClasses = computed(() => {
34
+ if (props.color === 'indigo' && !props.light) {
35
+ return 'bg-indigo-800 dark:bg-indigo-200 text-white dark:text-black'
36
+ }
37
+
38
+ if (props.color === 'gray' && props.light) {
39
+ return 'bg-gray-100 dark:bg-gray-900 text-gray-600 dark:text-gray-400'
40
+ }
41
+
42
+ return 'bg-gray-900 text-white dark:bg-gray-100 dark:text-gray-900'
43
+ })
44
+
15
45
  const emit = defineEmits<{
16
46
  navigate: [event: MouseEvent, direction: BaseDividerNavigateDirection]
17
47
  }>()
@@ -64,19 +94,21 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
64
94
 
65
95
  <div
66
96
  class="flex-1 border-t"
67
- :class="{
68
- 'border-dashed': borderStyle === 'dashed',
69
- 'border-dotted': borderStyle === 'dotted',
70
- 'border-gray-900 dark:border-gray-300': pill,
71
- 'border-gray-300 dark:border-gray-700': !pill,
72
- }"
97
+ :class="[
98
+ pillLineBorderClasses,
99
+ {
100
+ 'border-dashed': borderStyle === 'dashed',
101
+ 'border-dotted': borderStyle === 'dotted',
102
+ },
103
+ ]"
73
104
  style="height: 1px"
74
105
  />
75
106
  </div>
76
107
 
77
108
  <span
78
109
  v-if="pill && title && !loading"
79
- class="mx-3 rounded-full bg-gray-900 px-3 py-1 text-xs text-white font-medium tracking-wide uppercase dark:bg-gray-100 dark:text-gray-900"
110
+ class="mx-3 rounded-full px-3 py-1 text-xs font-medium tracking-wide"
111
+ :class="pillClasses"
80
112
  >{{ title }}</span>
81
113
 
82
114
  <BaseText
@@ -89,12 +121,13 @@ function onNavigate(event: MouseEvent, direction: BaseDividerNavigateDirection)
89
121
  <div class="flex items-center">
90
122
  <div
91
123
  class="flex-1 border-t"
92
- :class="{
93
- 'border-dashed': borderStyle === 'dashed',
94
- 'border-dotted': borderStyle === 'dotted',
95
- 'border-gray-900 dark:border-gray-300': pill,
96
- 'border-gray-300 dark:border-gray-700': !pill,
97
- }"
124
+ :class="[
125
+ pillLineBorderClasses,
126
+ {
127
+ 'border-dashed': borderStyle === 'dashed',
128
+ 'border-dotted': borderStyle === 'dotted',
129
+ },
130
+ ]"
98
131
  style="height: 1px"
99
132
  />
100
133
 
@@ -16,7 +16,7 @@ defineSlots<{
16
16
  <template>
17
17
  <component
18
18
  :is="tag"
19
- class="select-text tracking-tight normal-case"
19
+ class="select-text tracking-tight"
20
20
  :class="{
21
21
  'justify-start': alignment === 'left',
22
22
  'justify-center text-center': alignment === 'center',
@@ -14,7 +14,7 @@ defineSlots<{
14
14
 
15
15
  <template>
16
16
  <p
17
- class="select-text font-medium leading-relaxed tracking-tight normal-case"
17
+ class="select-text font-medium leading-relaxed tracking-tight"
18
18
  :class="{
19
19
  'text-center': alignment === 'center',
20
20
  'text-left': alignment === 'left',
@@ -88,7 +88,7 @@ function onClose(event: MouseEvent) {
88
88
  :size="size"
89
89
  />
90
90
 
91
- <div class="flex flex-1 flex-col font-semibold normal-case">
91
+ <div class="flex flex-1 flex-col font-semibold">
92
92
  <div
93
93
  class="rounded-2xl text-left transition duration-300"
94
94
  :class="{
@@ -48,7 +48,7 @@ onKeyStroke(
48
48
  <template>
49
49
  <kbd
50
50
  class="h-3.5 min-w-3.5 inline-flex items-center justify-center border border-current/50 rounded px-0.5 text-2xs leading-none"
51
- :class="{ 'font-mono uppercase': shortcut.toLowerCase() !== 'enter' }"
51
+ :class="{ 'font-mono': shortcut.toLowerCase() !== 'enter' }"
52
52
  >
53
53
  <Icon
54
54
  v-if="shortcut.toLowerCase() === 'enter'"
@@ -21,7 +21,6 @@ const props = withDefaults(defineProps<BaseTag>(), {
21
21
  text: '',
22
22
  to: undefined,
23
23
  truncate: false,
24
- uppercase: true,
25
24
  })
26
25
 
27
26
  const emit = defineEmits<{
@@ -112,7 +111,6 @@ function onRemove(event: MouseEvent) {
112
111
  'cursor-pointer': clickable,
113
112
  'rounded-md': !rounded,
114
113
  'rounded-full': rounded,
115
- 'uppercase': uppercase,
116
114
  'border-gray-200 dark:border-gray-800 bg-gray-200 dark:bg-gray-800 text-gray-800 dark:text-gray-200': (color === 'black' && light && !active) || removable,
117
115
  'border-gray-900 dark:border-gray-100 bg-gray-900 dark:bg-gray-100 text-white dark:text-black': color === 'black' && (!light || active),
118
116
  'border-gray-700 dark:border-gray-300 bg-gray-100 dark:bg-gray-900 text-gray-700 dark:text-gray-300': color === 'gray' && light && !active,
@@ -31,7 +31,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
31
31
 
32
32
  <template>
33
33
  <div
34
- class="group flex select-none items-center border border-gray-200 rounded-full bg-white px-3 py-2 text-base text-gray-800 font-normal uppercase shadow-sm transition-colors dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
34
+ class="group flex select-none items-center border border-gray-200 rounded-full bg-white px-3 py-2 text-base text-gray-800 font-normal shadow-sm transition-colors dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
35
35
  :class="{ 'cursor-pointer': hasClose }"
36
36
  role="button"
37
37
  tabindex="0"
@@ -47,7 +47,7 @@ function onClose(event: KeyboardEvent | MouseEvent) {
47
47
 
48
48
  <button
49
49
  v-if="action"
50
- class="ml-2 min-h-6 inline-flex items-center justify-center gap-1.25 rounded-md px-2 py-0.5 uppercase transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-800"
50
+ class="ml-2 min-h-6 inline-flex items-center justify-center gap-1.25 rounded-md px-2 py-0.5 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-offset-white dark:focus-visible:ring-offset-gray-800"
51
51
  :class="{
52
52
  'bg-red-100 text-red-700 hover:bg-red-200 focus-visible:ring-red-500 dark:bg-red-400/15 dark:text-red-400 dark:hover:bg-red-400/25': status === 'error',
53
53
  'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 focus-visible:ring-indigo-500 dark:bg-indigo-400/15 dark:text-indigo-400 dark:hover:bg-indigo-400/25': status === 'info',
@@ -13,7 +13,6 @@ withDefaults(defineProps<Omit<FieldCheckbox, 'modelValue'>>(), {
13
13
  required: false,
14
14
  size: 'base',
15
15
  truncate: false,
16
- uppercase: false,
17
16
  validation: undefined,
18
17
  })
19
18
 
@@ -24,7 +24,6 @@ const props = withDefaults(defineProps<Omit<FieldInput, 'modelValue'>>(), {
24
24
  slugOnly: false,
25
25
  trim: false,
26
26
  type: 'text',
27
- uppercase: false,
28
27
  validation: undefined,
29
28
  })
30
29
 
@@ -179,14 +178,12 @@ defineExpose({ focus })
179
178
  :id="id"
180
179
  ref="input"
181
180
  :autocomplete="autocomplete ? 'on' : 'off'"
182
- class="h-full w-full appearance-none items-center rounded-lg outline-none placeholder-gray-600 dark:placeholder-gray-400"
181
+ class="h-full w-full appearance-none items-center rounded-lg font-medium tracking-tight normal-case outline-none placeholder-gray-600 dark:placeholder-gray-400"
183
182
  :class="{
184
183
  'field-disabled': disabled,
185
184
  'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
186
185
  'text-gray-900 dark:text-gray-100': !lineThrough,
187
186
  'text-gray-500 dark:text-gray-500 line-through': lineThrough,
188
- 'font-medium tracking-tight uppercase': uppercase,
189
- 'font-normal': !uppercase,
190
187
  'text-center': alignment === 'center',
191
188
  'text-left': alignment === 'left',
192
189
  'text-right': alignment === 'right',
@@ -37,6 +37,10 @@ const textSize = computed<BaseSize>(() => {
37
37
  })
38
38
 
39
39
  function onClick(event: MouseEvent) {
40
+ if (!isClickable.value) {
41
+ return
42
+ }
43
+
40
44
  emit('click', event)
41
45
  }
42
46
 
@@ -79,7 +83,7 @@ function onKeyDown(event: KeyboardEvent) {
79
83
  :for="forField || undefined"
80
84
  :tabindex="isClickable && !forField ? 0 : undefined"
81
85
  :type="isClickable && !forField ? 'button' : undefined"
82
- @click="isClickable ? onClick : undefined"
86
+ @click="onClick"
83
87
  @keydown="onKeyDown"
84
88
  >
85
89
  <BaseIcon
@@ -247,14 +247,14 @@ function selectOption(event: MouseEvent, value: string) {
247
247
  :icon="selectedOption.icon"
248
248
  />
249
249
 
250
- <span class="flex-1 truncate font-medium uppercase">
250
+ <span class="flex-1 truncate font-medium">
251
251
  {{ selectedOption.text }}
252
252
  </span>
253
253
  </template>
254
254
 
255
255
  <span
256
256
  v-else-if="placeholder"
257
- class="flex-1 truncate text-gray-600 font-medium uppercase dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-gray-100"
257
+ class="flex-1 truncate text-gray-600 font-medium dark:text-gray-400 group-hover:text-gray-900 dark:group-hover:text-gray-100"
258
258
  >
259
259
  {{ placeholder }}
260
260
  </span>
@@ -67,7 +67,7 @@ function onFieldClick(event: MouseEvent) {
67
67
  ref="textarea"
68
68
  v-model="modelValue"
69
69
  v-bind="{ 'data-enable-grammarly': 'false' }"
70
- class="w-full flex-1 appearance-none rounded-lg text-gray-900 font-medium tracking-tight uppercase outline-none dark:text-gray-100 placeholder-gray-600 dark:placeholder-gray-400"
70
+ class="w-full flex-1 appearance-none rounded-lg text-gray-900 font-medium tracking-tight normal-case outline-none dark:text-gray-100 placeholder-gray-600 dark:placeholder-gray-400"
71
71
  :class="{
72
72
  'field-disabled': disabled,
73
73
  'focus:placeholder-gray-900 hover:placeholder-gray-900 dark:focus:placeholder-gray-100 dark:hover:placeholder-gray-100': !disabled,
@@ -5,6 +5,7 @@ withDefaults(defineProps<LayoutBottomSheet>(), { title: undefined })
5
5
 
6
6
  defineSlots<{
7
7
  default?: () => VNode[]
8
+ footer?: () => VNode[]
8
9
  hero?: () => VNode[]
9
10
  }>()
10
11
 
@@ -215,7 +216,7 @@ watch(visible, (isVisible) => {
215
216
  ref="panelRef"
216
217
  :aria-label="title"
217
218
  aria-modal="true"
218
- class="layout-bottom-sheet fixed bottom-0 left-0 right-0 z-[60] mx-auto max-h-[85dvh] max-w-screen-md flex flex-col rounded-t-2xl bg-white text-gray-900 shadow-lg outline-none safe-bottom dark:bg-gray-900 dark:text-gray-100"
219
+ class="layout-bottom-sheet fixed bottom-0 left-0 right-0 z-[60] mx-auto max-h-[85dvh] max-w-screen-md flex flex-col rounded-t-2xl bg-white text-gray-900 app-text shadow-lg outline-none safe-bottom dark:bg-gray-900 dark:text-gray-100"
219
220
  :class="{
220
221
  'transition-none motion-reduce:transition-none': drag.isDragging,
221
222
  'transition-transform duration-200 ease motion-reduce:transition-none motion-reduce:duration-0': !drag.isDragging && (drag.closing || drag.offset > 0),
@@ -241,7 +242,8 @@ watch(visible, (isVisible) => {
241
242
 
242
243
  <div
243
244
  ref="contentRef"
244
- class="min-h-0 flex-1 overflow-y-auto px-5 pb-8"
245
+ class="min-h-0 flex-1 overflow-y-auto px-5"
246
+ :class="$slots.footer ? 'pb-4' : 'pb-6'"
245
247
  @pointercancel="onPointerUp"
246
248
  @pointerdown="onContentPointerDown"
247
249
  @pointermove="onPointerMove"
@@ -249,6 +251,13 @@ watch(visible, (isVisible) => {
249
251
  >
250
252
  <slot />
251
253
  </div>
254
+
255
+ <div
256
+ v-if="$slots.footer"
257
+ class="flex shrink-0 justify-center px-5 pb-6 pt-2"
258
+ >
259
+ <slot name="footer" />
260
+ </div>
252
261
  </div>
253
262
  </Transition>
254
263
  </Teleport>
@@ -52,7 +52,7 @@ function onClose() {
52
52
  ref="panelRef"
53
53
  :aria-label="title"
54
54
  aria-modal="true"
55
- class="pointer-events-auto max-h-[85dvh] max-w-md w-full overflow-y-auto rounded-2xl bg-white p-5 text-gray-900 shadow-lg outline-none dark:bg-gray-900 dark:text-gray-100"
55
+ class="pointer-events-auto max-h-[85dvh] max-w-md w-full overflow-y-auto rounded-2xl bg-white p-5 text-gray-900 app-text shadow-lg outline-none dark:bg-gray-900 dark:text-gray-100"
56
56
  role="dialog"
57
57
  tabindex="-1"
58
58
  >
@@ -131,8 +131,10 @@ export type BaseColor
131
131
 
132
132
  export interface BaseDivider {
133
133
  borderStyle?: BaseDividerBorderStyle
134
+ color?: BaseColor
134
135
  hideNext?: boolean
135
136
  hidePrevious?: boolean
137
+ light?: boolean
136
138
  loading?: boolean
137
139
  navigable?: boolean
138
140
  pill?: boolean
@@ -346,7 +348,6 @@ export interface BaseTag {
346
348
  text: BaseTextText
347
349
  to?: RouteLocationNamedI18n
348
350
  truncate?: boolean
349
- uppercase?: boolean
350
351
  }
351
352
 
352
353
  export interface BaseTags {
@@ -34,7 +34,6 @@ export interface FieldCheckbox {
34
34
  required?: boolean
35
35
  size?: FieldSize
36
36
  truncate?: boolean
37
- uppercase?: boolean
38
37
  validation?: VuelidateValidation
39
38
  }
40
39
 
@@ -72,7 +71,6 @@ export interface FieldInput {
72
71
  slugOnly?: boolean
73
72
  trim?: boolean
74
73
  type?: FieldInputType
75
- uppercase?: boolean
76
74
  validation?: VuelidateValidation
77
75
  }
78
76
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "1.4.56",
3
+ "version": "1.5.1",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",
package/uno.config.ts CHANGED
@@ -24,7 +24,10 @@ export const rules: [string, Record<string, string>][] = [
24
24
  ['safe-bottom', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
25
25
  ]
26
26
 
27
- export const shortcuts: Record<string, string> = { 'field-disabled': 'opacity-50 cursor-not-allowed' }
27
+ export const shortcuts: Record<string, string> = {
28
+ 'app-text': 'font-bold leading-relaxed tracking-tight uppercase',
29
+ 'field-disabled': 'opacity-50 cursor-not-allowed',
30
+ }
28
31
 
29
32
  export const theme = {
30
33
  container: {