@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.
- package/dist/{BuiTable.vue_vue_type_script_setup_true_lang-C3hr0DBS.js → BuiTable.vue_vue_type_script_setup_true_lang-Dd_dkcy4.js} +5 -5
- package/dist/components/input/BuiInput.vue.d.ts +3 -1
- package/dist/components/input/BuiPasswordInput.vue.d.ts +2 -0
- package/dist/components/input/index.js +151 -98
- package/dist/components/table/BuiTable.js +1 -1
- package/dist/components/table/index.js +365 -358
- package/dist/index.js +1 -1
- package/dist/lib/useResizeColumns.d.ts +4 -2
- package/dist/lib/useResizeColumns.js +57 -53
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/input/BuiInput.vue +41 -1
- package/src/components/input/BuiPasswordInput.vue +15 -6
- package/src/components/table/BuiDataTable.vue +11 -3
- package/src/components/table/BuiTable.vue +1 -1
- package/src/lib/useResizeColumns.ts +20 -3
- package/src/stories/BuiInput.stories.ts +20 -0
|
@@ -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
|
-
<
|
|
34
|
+
<BuiButton
|
|
29
35
|
@click="togglePasswordShow"
|
|
30
|
-
|
|
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
|
|
43
|
+
class="pointer-events-none h-4 w-4 shrink-0 cursor-pointer"
|
|
35
44
|
/>
|
|
36
|
-
<EyeOffIcon v-else class="h-4 w-4
|
|
37
|
-
</
|
|
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="
|
|
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
|
|
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
|
+
}
|