@soft-stech/bootsman-ui-shadcn 2.0.13 → 2.0.15

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.
@@ -2,8 +2,10 @@
2
2
  import { FORM_READONLY_INJECTION_KEY } from '@/components/form'
3
3
  import { cn } from '@/lib/utils'
4
4
  import { useVModel } from '@vueuse/core'
5
- import { inject, ref, toRef, type HTMLAttributes } from 'vue'
5
+ import { inject, ref, toRef, useTemplateRef, type HTMLAttributes } from 'vue'
6
6
  import { inputVariants } from '.'
7
+ import BuiButton from '@/components/button/BuiButton.vue'
8
+ import { X } from 'lucide-vue-next'
7
9
 
8
10
  const props = defineProps<{
9
11
  defaultValue?: string | number
@@ -29,6 +31,7 @@ const modelValue = useVModel(props, 'modelValue', emits, {
29
31
  const readonlyContext = inject(FORM_READONLY_INJECTION_KEY, toRef(false))
30
32
 
31
33
  const isFocused = ref<boolean>(false)
34
+ const searchInput = useTemplateRef<HTMLElement>('searchInput')
32
35
 
33
36
  const handleFocus = () => {
34
37
  isFocused.value = true
@@ -44,10 +47,47 @@ const handleKeyDown = (e: KeyboardEvent) => {
44
47
  }
45
48
  }
46
49
  }
50
+
51
+ const handleClear = () => {
52
+ if (searchInput.value) {
53
+ searchInput.value.focus()
54
+ modelValue.value = ''
55
+ }
56
+ }
47
57
  </script>
48
58
 
49
59
  <template>
60
+ <div v-if="props.type === 'search'" class="group relative flex flex-row items-center">
61
+ <input
62
+ ref="searchInput"
63
+ v-model="modelValue"
64
+ :class="
65
+ cn(
66
+ inputVariants({ variant }),
67
+ props.class ?? '',
68
+ 'pr-6',
69
+ !props.variant || props.variant === 'default' ? 'group-hover:border-primary' : ''
70
+ )
71
+ "
72
+ :readonly="readonlyContext || props.readonly"
73
+ :disabled="props.disabled"
74
+ :placeholder="props.placeholder"
75
+ type="text"
76
+ @focus="handleFocus"
77
+ @blur="handleBlur"
78
+ @keydown="handleKeyDown"
79
+ />
80
+ <BuiButton
81
+ v-if="modelValue && modelValue.toString().length > 0"
82
+ variant="none"
83
+ class="hover:text-primary text-foreground invisible absolute top-2 right-2 h-fit w-fit p-[1px] group-hover:visible"
84
+ @click="handleClear"
85
+ >
86
+ <X :size="14" class="pointer-events-none shrink-0" />
87
+ </BuiButton>
88
+ </div>
50
89
  <input
90
+ v-else
51
91
  v-model="modelValue"
52
92
  :class="cn(inputVariants({ variant }), props.class ?? '')"
53
93
  :readonly="readonlyContext || props.readonly"
@@ -2,10 +2,13 @@
2
2
  import { BuiInput } from '@/components/input/index'
3
3
  import { cn } from '@/lib/utils'
4
4
  import { EyeIcon, EyeOffIcon } from 'lucide-vue-next'
5
- import { ref } from 'vue'
5
+ import { computed, ref } from 'vue'
6
+ import BuiButton from '@/components/button/BuiButton.vue'
6
7
 
7
8
  const props = defineProps<{
8
9
  defaultValue?: 'password' | 'text'
10
+ showPasswordTranslation?: string
11
+ hidePasswordTranslation?: string
9
12
  }>()
10
13
 
11
14
  defineOptions({
@@ -20,20 +23,26 @@ function togglePasswordShow() {
20
23
  type.value = 'password'
21
24
  }
22
25
  }
26
+
27
+ const showPasswordText = computed(() => props.showPasswordTranslation ?? 'Show password')
28
+ const hidePasswordText = computed(() => props.hidePasswordTranslation ?? 'Hide password')
23
29
  </script>
24
30
 
25
31
  <template>
26
32
  <div class="relative flex">
27
33
  <BuiInput v-bind="$attrs" :type="type" :class="cn($attrs.class ?? '', 'pr-8')" />
28
- <span
34
+ <BuiButton
29
35
  @click="togglePasswordShow"
30
- class="absolute top-0 right-3 flex h-full items-center justify-center"
36
+ variant="none"
37
+ class="text-muted-foreground hover:text-foreground absolute top-0 right-3 flex h-full w-fit items-center justify-center p-0"
38
+ :aria-label="type === 'password' ? showPasswordText : hidePasswordText"
39
+ :aria-pressed="type !== 'password'"
31
40
  >
32
41
  <EyeIcon
33
42
  v-if="type === 'password'"
34
- class="h-4 w-4 cursor-pointer opacity-[0.56] hover:opacity-100"
43
+ class="pointer-events-none h-4 w-4 shrink-0 cursor-pointer"
35
44
  />
36
- <EyeOffIcon v-else class="h-4 w-4 cursor-pointer opacity-[0.56] hover:opacity-100" />
37
- </span>
45
+ <EyeOffIcon v-else class="pointer-events-none h-4 w-4 shrink-0 cursor-pointer" />
46
+ </BuiButton>
38
47
  </div>
39
48
  </template>
@@ -268,7 +268,9 @@ const {
268
268
  handleResizeControlMouseDown,
269
269
  handleResizeControlMouseUp,
270
270
  setInitialColumnWidths,
271
- setProvidedCellWidths
271
+ setProvidedCellWidths,
272
+ isMouseDownOnHandler,
273
+ isMouseUpOnHandler
272
274
  } = useResizeColumns()
273
275
 
274
276
  onBeforeMount(() => {
@@ -383,6 +385,10 @@ watch(
383
385
  )
384
386
 
385
387
  const handleHeaderCellSorting = (header: Header<TData, unknown>) => {
388
+ if (isMouseDownOnHandler.value && !isMouseUpOnHandler.value) {
389
+ return false
390
+ }
391
+
386
392
  if (getHeaderCellSortingButton(header)) {
387
393
  header.column.toggleSorting(header.column.getIsSorted() === 'asc')
388
394
  }
@@ -466,11 +472,13 @@ const handleHeaderCellSorting = (header: Header<TData, unknown>) => {
466
472
  enableColumnResizing && index < tableHeaders.length - 1 && header.column.getCanResize()
467
473
  "
468
474
  @dblclick="resetCells"
469
- @mousedown="() => handleResizeControlMouseDown(header.id, props.enableColumnResizing)"
475
+ @mousedown.self="
476
+ (e: Event) => handleResizeControlMouseDown(e, header.id, props.enableColumnResizing)
477
+ "
470
478
  @click.stop
471
479
  :className="
472
480
  cn(
473
- 'absolute top-0 right-0 h-full w-1 bg-muted-foreground opacity-0 cursor-col-resize select-none touch-none hover:opacity-50',
481
+ 'resize-handler absolute top-0 right-0 h-full w-1 bg-muted-foreground opacity-0 cursor-col-resize select-none touch-none hover:opacity-50',
474
482
  isResizing && resizingCellId === header.id ? 'bg-primary opacity-50' : ''
475
483
  )
476
484
  "
@@ -8,7 +8,7 @@ const props = defineProps<{ class?: string }>()
8
8
  <template>
9
9
  <BuiScrollArea class="border-border/16 w-full grow overflow-auto rounded-sm border">
10
10
  <slot name="columnVisibility" />
11
- <div class="min-h-[90px]">
11
+ <div class="flex min-h-[90px] grow flex-col">
12
12
  <table :class="cn('h-full w-full caption-top text-sm', props.class)">
13
13
  <slot />
14
14
  </table>
@@ -56,7 +56,18 @@ export function useResizeColumns() {
56
56
  return undefined
57
57
  }
58
58
 
59
- const handleResizeControlMouseDown = (cellId: string, enableColumnResizing: boolean) => {
59
+ const isMouseDownOnHandler = ref<boolean>(false)
60
+ const isMouseUpOnHandler = ref<boolean>(false)
61
+
62
+ const handleResizeControlMouseDown = (
63
+ e: Event,
64
+ cellId: string,
65
+ enableColumnResizing: boolean
66
+ ) => {
67
+ const targetHTMLElement = e.target as HTMLElement
68
+ isMouseDownOnHandler.value =
69
+ targetHTMLElement.className.includes && targetHTMLElement.className.includes('resize-handler')
70
+
60
71
  if (!enableColumnResizing) return
61
72
 
62
73
  isResizing.value = true
@@ -76,7 +87,11 @@ export function useResizeColumns() {
76
87
  }
77
88
  }
78
89
 
79
- const handleResizeControlMouseUp = () => {
90
+ const handleResizeControlMouseUp = (e: Event) => {
91
+ const targetHTMLElement = e.target as HTMLElement
92
+ isMouseUpOnHandler.value =
93
+ targetHTMLElement.className.includes && targetHTMLElement.className.includes('resize-handler')
94
+
80
95
  if (!isResizing.value) return
81
96
 
82
97
  isResizing.value = false
@@ -221,6 +236,8 @@ export function useResizeColumns() {
221
236
  handleResizeControlMouseUp,
222
237
  resetCells,
223
238
  setInitialColumnWidths,
224
- setProvidedCellWidths
239
+ setProvidedCellWidths,
240
+ isMouseDownOnHandler,
241
+ isMouseUpOnHandler
225
242
  }
226
243
  }
@@ -69,3 +69,23 @@ export const ColorPicker: Story = {
69
69
  `
70
70
  })
71
71
  }
72
+
73
+ export const Search: Story = {
74
+ render: (args) => ({
75
+ components: { BuiInput },
76
+ setup() {
77
+ args = {
78
+ ...args,
79
+ type: 'search',
80
+ placeholder: 'Filter'
81
+ }
82
+
83
+ return { args }
84
+ },
85
+ template: `
86
+ <div class="grid w-full max-w-sm items-center gap-1.5">
87
+ <BuiInput id="search" v-bind="args" />
88
+ </div>
89
+ `
90
+ })
91
+ }