@saasmakers/ui 2.0.21 → 2.0.23

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.
@@ -77,7 +77,6 @@ function onClick(event: MouseEvent) {
77
77
  />
78
78
 
79
79
  <BaseText
80
- bold
81
80
  class="tracking-tight uppercase"
82
81
  :size="size === 'lg' ? 'xs' : '2xs'"
83
82
  :text="confirming ? t('confirm') : text"
@@ -5,10 +5,10 @@ import type { BaseAvatar } from '../../types/bases'
5
5
  const props = withDefaults(defineProps<BaseAvatar>(), {
6
6
  bordered: true,
7
7
  borderWidth: 3,
8
- circular: true,
9
8
  editable: false,
10
9
  maxSizeMb: undefined,
11
10
  shadow: true,
11
+ shape: 'circle',
12
12
  src: undefined,
13
13
  to: undefined,
14
14
  })
@@ -102,7 +102,8 @@ function onMouseLeave() {
102
102
  :aria-label="editable ? t('edit') : undefined"
103
103
  class="relative h-full w-full overflow-hidden"
104
104
  :class="{
105
- 'rounded-full': circular,
105
+ 'rounded-full': shape === 'circle',
106
+ 'rounded-lg': shape === 'square',
106
107
  'shadow': shadow,
107
108
  'cursor-pointer': isClickable,
108
109
  }"
@@ -116,7 +117,8 @@ function onMouseLeave() {
116
117
  <img
117
118
  class="h-full w-full object-cover drag-none"
118
119
  :class="{
119
- 'rounded-full': circular,
120
+ 'rounded-full': shape === 'circle',
121
+ 'rounded-lg': shape === 'square',
120
122
  'border-white dark:border-white': bordered,
121
123
  'border-2': bordered && borderWidth === 2,
122
124
  'border-3': bordered && borderWidth === 3,
@@ -4,7 +4,6 @@ import type { BaseButton } from '../../types/bases'
4
4
 
5
5
  const props = withDefaults(defineProps<BaseButton>(), {
6
6
  background: true,
7
- circular: false,
8
7
  color: 'black',
9
8
  confirmation: false,
10
9
  disabled: false,
@@ -15,6 +14,7 @@ const props = withDefaults(defineProps<BaseButton>(), {
15
14
  light: false,
16
15
  loading: false,
17
16
  reverse: false,
17
+ shape: 'default',
18
18
  size: 'base',
19
19
  text: '',
20
20
  to: undefined,
@@ -33,6 +33,10 @@ defineSlots<{
33
33
  const confirming = ref(false)
34
34
  const { t } = useI18n()
35
35
 
36
+ const hasFixedSize = computed(() => {
37
+ return props.shape === 'square' || props.shape === 'circle'
38
+ })
39
+
36
40
  function onClick(event: MouseEvent) {
37
41
  if (!props.loading) {
38
42
  if (props.confirmation) {
@@ -56,21 +60,21 @@ function onClick(event: MouseEvent) {
56
60
  :is="to ? NuxtLinkLocale : 'button'"
57
61
  class="relative inline-block select-none appearance-none border uppercase focus-visible:outline-none"
58
62
  :class="{
59
- 'flex items-center justify-center': circular,
63
+ 'flex items-center justify-center': hasFixedSize,
60
64
  'cursor-not-allowed opacity-50': disabled,
61
65
  'cursor-wait': loading,
62
66
  'cursor-pointer': !loading,
63
67
  'w-full': fullWidth,
64
68
  'bg-transparent shadow-none': light,
65
69
  'shadow-sm': !light,
66
- 'rounded-lg': !circular,
67
- 'rounded-full': circular,
68
- 'h-6 w-6': size === '2xs' && circular,
69
- 'h-8 w-8': size === 'xs' && circular && !confirming,
70
- 'h-10 w-10': size === 'sm' && circular && !confirming,
71
- 'h-12 w-12': size === 'base' && circular && !confirming,
72
- 'h-14 w-14': size === 'lg' && circular && !confirming,
73
- 'h-16 w-16': size === 'xl' && circular && !confirming,
70
+ 'rounded-lg': shape !== 'circle',
71
+ 'rounded-full': shape === 'circle',
72
+ 'h-6 w-6': size === '2xs' && hasFixedSize,
73
+ 'h-8 w-8': size === 'xs' && hasFixedSize && !confirming,
74
+ 'h-10 w-10': size === 'sm' && hasFixedSize && !confirming,
75
+ 'h-12 w-12': size === 'base' && hasFixedSize && !confirming,
76
+ 'h-14 w-14': size === 'lg' && hasFixedSize && !confirming,
77
+ 'h-16 w-16': size === 'xl' && hasFixedSize && !confirming,
74
78
  'border-gray-900 dark:border-gray-100 bg-gray-900 dark:bg-gray-100 text-white dark:text-black': color === 'black' && !light,
75
79
  'hover:bg-black dark:hover:bg-white hover:text-white dark:hover:text-black': color === 'black' && !light && !disabled && !loading,
76
80
  'border-gray-900 dark:border-gray-100 text-gray-900 dark:text-gray-100': color === 'black' && light,
@@ -94,12 +98,12 @@ function onClick(event: MouseEvent) {
94
98
  'border-white dark:border-gray-900 text-white dark:text-black': color === 'white' && light,
95
99
  'hover:border-black dark:hover:border-white hover:text-black dark:hover:text-white': color !== 'white' && light && !disabled && !loading,
96
100
  'hover:bg-white dark:hover:bg-gray-900 hover:text-gray-800 dark:hover:text-gray-200': color === 'white' && light && !disabled && !loading,
97
- 'h-6 px-3': size === '2xs' && (!circular || confirming),
98
- 'h-8 px-4': size === 'xs' && (!circular || confirming),
99
- 'h-10 px-5': size === 'sm' && (!circular || confirming),
100
- 'h-12 px-6': size === 'base' && (!circular || confirming),
101
- 'h-13 px-6.5': size === 'lg' && (!circular || confirming),
102
- 'h-14 px-7': size === 'xl' && (!circular || confirming),
101
+ 'h-6 px-3': size === '2xs' && (!hasFixedSize || confirming),
102
+ 'h-8 px-4': size === 'xs' && (!hasFixedSize || confirming),
103
+ 'h-10 px-5': size === 'sm' && (!hasFixedSize || confirming),
104
+ 'h-12 px-6': size === 'base' && (!hasFixedSize || confirming),
105
+ 'h-13 px-6.5': size === 'lg' && (!hasFixedSize || confirming),
106
+ 'h-14 px-7': size === 'xl' && (!hasFixedSize || confirming),
103
107
  }"
104
108
  :disabled="disabled"
105
109
  :to="to"
@@ -27,6 +27,8 @@ const textSize = computed<BaseSize>(() => {
27
27
  switch (props.size) {
28
28
  case 'lg':
29
29
  return 'base'
30
+ case 'base':
31
+ return 'sm'
30
32
  case 'sm':
31
33
  return 'xs'
32
34
  default:
@@ -14,7 +14,7 @@ const dragMoveThreshold = 8
14
14
  const visible = defineModel<boolean>({ default: false })
15
15
  const contentRef = ref<HTMLElement>()
16
16
  const panelRef = ref<HTMLElement>()
17
- const { release: onAfterLeave } = useDialog(visible, panelRef, { ignoreClass: 'layout-bottom-sheet' })
17
+ const { release: onAfterLeave } = useDialog(visible, panelRef)
18
18
 
19
19
  const drag = reactive({
20
20
  closing: false,
@@ -1,17 +1,10 @@
1
- interface UseDialogOptions {
2
- ignoreClass: string
3
- }
4
-
5
1
  export default function useDialog(
6
2
  visible: Ref<boolean>,
7
3
  panelRef: Ref<HTMLElement | undefined>,
8
- options: UseDialogOptions,
9
4
  ) {
10
5
  const focusableSelector = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
11
- const fixedElementPadding = new Map<HTMLElement, string>()
12
6
 
13
- let savedBodyOverflow = ''
14
- let savedBodyPaddingRight = ''
7
+ let savedRootOverflow = ''
15
8
 
16
9
  function getFocusableElements(container: HTMLElement) {
17
10
  return Array.from(container.querySelectorAll<HTMLElement>(focusableSelector))
@@ -20,39 +13,12 @@ export default function useDialog(
20
13
  })
21
14
  }
22
15
 
16
+ // Rely on the global `scrollbar-gutter: stable` (html/body) to keep the gutter
17
+ // reserved while the scroll container is locked, so no manual padding
18
+ // compensation is needed and no horizontal layout shift occurs.
23
19
  function lockBodyScroll() {
24
- const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth
25
-
26
- savedBodyOverflow = document.body.style.overflow
27
- savedBodyPaddingRight = document.body.style.paddingRight
28
- document.body.style.overflow = 'hidden'
29
-
30
- if (scrollbarWidth > 0) {
31
- document.body.style.paddingRight = `${scrollbarWidth}px`
32
-
33
- for (const element of document.querySelectorAll('body *')) {
34
- if (!(element instanceof HTMLElement)) {
35
- continue
36
- }
37
-
38
- // Skip the dialog's own teleported elements so they are not padded.
39
- if (element.classList.contains(options.ignoreClass)) {
40
- continue
41
- }
42
-
43
- const { position } = getComputedStyle(element)
44
-
45
- if (position !== 'fixed' && position !== 'sticky') {
46
- continue
47
- }
48
-
49
- fixedElementPadding.set(element, element.style.paddingRight)
50
-
51
- const currentPadding = Number.parseFloat(getComputedStyle(element).paddingRight) || 0
52
-
53
- element.style.paddingRight = `${currentPadding + scrollbarWidth}px`
54
- }
55
- }
20
+ savedRootOverflow = document.documentElement.style.overflow
21
+ document.documentElement.style.overflow = 'hidden'
56
22
  }
57
23
 
58
24
  function onFocusTrap(event: KeyboardEvent) {
@@ -90,14 +56,7 @@ export default function useDialog(
90
56
  }
91
57
 
92
58
  function unlockBodyScroll() {
93
- document.body.style.overflow = savedBodyOverflow
94
- document.body.style.paddingRight = savedBodyPaddingRight
95
-
96
- for (const [element, paddingRight] of fixedElementPadding) {
97
- element.style.paddingRight = paddingRight
98
- }
99
-
100
- fixedElementPadding.clear()
59
+ document.documentElement.style.overflow = savedRootOverflow
101
60
  }
102
61
 
103
62
  // Restores scroll lock and focus trap once the leave transition has finished.
@@ -24,13 +24,17 @@ export interface BaseAlert {
24
24
  // Global types for bases
25
25
  export type BaseAlignment = 'center' | 'left' | 'right'
26
26
 
27
+ export type BaseButtonShape = 'circle' | 'default' | 'square'
28
+
29
+ export type BaseAvatarShape = 'circle' | 'square'
30
+
27
31
  export interface BaseAvatar {
28
32
  bordered?: boolean
29
33
  borderWidth?: number
30
- circular?: boolean
31
34
  editable?: boolean
32
35
  maxSizeMb?: number
33
36
  shadow?: boolean
37
+ shape?: BaseAvatarShape
34
38
  src?: string
35
39
  to?: RouteLocationNamedI18n
36
40
  }
@@ -49,7 +53,6 @@ export type BaseBorderedColor = 'black' | 'gray'
49
53
 
50
54
  export interface BaseButton {
51
55
  background?: boolean
52
- circular?: boolean
53
56
  color?: BaseColor
54
57
  confirmation?: boolean
55
58
  disabled?: boolean
@@ -60,6 +63,7 @@ export interface BaseButton {
60
63
  light?: boolean
61
64
  loading?: boolean
62
65
  reverse?: boolean
66
+ shape?: BaseButtonShape
63
67
  size?: BaseButtonSize
64
68
  text?: BaseTextText
65
69
  to?: RouteLocationNamedI18n
@@ -66,6 +66,8 @@ declare global {
66
66
  type BaseQuoteSize = import('./bases').BaseQuoteSize
67
67
  type BaseShortcut = import('./bases').BaseShortcut
68
68
  type BaseSize = import('./bases').BaseSize
69
+ type BaseButtonShape = import('./bases').BaseButtonShape
70
+ type BaseAvatarShape = import('./bases').BaseAvatarShape
69
71
  type BaseSpinner = import('./bases').BaseSpinner
70
72
  type BaseStatus = import('./bases').BaseStatus
71
73
  type BaseTag = import('./bases').BaseTag
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "2.0.21",
3
+ "version": "2.0.23",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",