@soft-stech/bootsman-ui-shadcn 1.5.5 → 1.5.7

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.
@@ -1,7 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { ref } from 'vue'
3
3
  import { z } from 'zod'
4
-
5
4
  import { cn } from '@/lib/utils'
6
5
  import { BuiButton } from '@/components/ui/button'
7
6
  import {
@@ -10,10 +9,11 @@ import {
10
9
  BuiCommandGroup,
11
10
  BuiCommandInput,
12
11
  BuiCommandItem,
13
- BuiCommandList
12
+ BuiCommandList,
13
+ BuiCommandNewItem
14
14
  } from '@/components/ui/command'
15
15
  import { BuiPopover, BuiPopoverContent, BuiPopoverTrigger } from '@/components/ui/popover'
16
- import { CheckIcon, ChevronsUpDownIcon, ChevronDown } from 'lucide-vue-next'
16
+ import { CheckIcon, ChevronsUpDownIcon, ChevronDown, CirclePlusIcon } from 'lucide-vue-next'
17
17
  import {
18
18
  BuiForm,
19
19
  BuiFormField,
@@ -34,14 +34,18 @@ const frameworks = [
34
34
 
35
35
  const open = ref(false)
36
36
  const value = ref<string>('')
37
+ const search = ref<string>('')
37
38
 
38
39
  const formSchema = toTypedSchema(
39
40
  z.object({
40
- namespace: z.string().min(2)
41
+ namespace: z.string().min(2),
42
+ groups: z.array(z.string())
41
43
  })
42
44
  )
43
45
  const isNamespacesPopoverOpen = ref(false)
46
+ const isGroupsPopoverOpen = ref(false)
44
47
  const namespaces = ['default', 'local', 'my-namespace']
48
+ const existingGroups = ref(['group-1', 'group-2', 'group-3'])
45
49
 
46
50
  // const filterFunction = (list: typeof frameworks, search: string) => list.filter(i => i.value.toLowerCase().includes(search.toLowerCase()))
47
51
  </script>
@@ -178,5 +182,118 @@ const namespaces = ['default', 'local', 'my-namespace']
178
182
  </BuiForm>
179
183
  </div>
180
184
  </Variant>
185
+
186
+ <Variant key="multi-with-create" title="Multiselect + create">
187
+ <div class="p-4">
188
+ <BuiForm :validation-schema="formSchema" :initial-values="{ groups: ['group-1'] }">
189
+ <BuiFormField name="groups" v-slot="{ meta, validate, value, setValue }">
190
+ <BuiFormItem class="w-full">
191
+ <BuiLabel :for="`groups`" class="flex gap-2">
192
+ <div class="flex">Groups</div>
193
+ </BuiLabel>
194
+
195
+ <BuiPopover
196
+ v-model:open="isGroupsPopoverOpen"
197
+ @update:open="
198
+ (isOpen) => {
199
+ if (!isOpen) {
200
+ validate()
201
+ }
202
+ }
203
+ "
204
+ >
205
+ <BuiPopoverTrigger as-child :id="`groups`">
206
+ <BuiButton
207
+ variant="outline"
208
+ size="lg"
209
+ role="combobox"
210
+ :aria-expanded="isGroupsPopoverOpen"
211
+ class="flex h-10 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-background px-3 py-2 text-sm text-inherit ring-offset-background placeholder:text-muted-foreground hover:bg-transparent focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 active:bg-transparent active:outline-none active:ring-0 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:min-w-0 [&>span]:truncate"
212
+ :class="!meta.valid && meta.validated ? '!border-destructive-foreground' : ''"
213
+ >
214
+ <span>
215
+ {{ value.length ? value.join(', ') : 'Select multiple or add' }}
216
+ </span>
217
+ <ChevronDown class="h-4 w-4 flex-shrink-0 opacity-50" />
218
+ </BuiButton>
219
+ </BuiPopoverTrigger>
220
+
221
+ <BuiPopoverContent class="w-[var(--radix-popper-anchor-width)] p-0">
222
+ <BuiCommand class="p-0">
223
+ <BuiCommandInput
224
+ class="h-9"
225
+ placeholder="Search or create new"
226
+ v-model="search"
227
+ @input="(event: Event) => (search = (event.target as HTMLInputElement).value)"
228
+ />
229
+ <BuiCommandEmpty class="flex w-full py-1 pl-1">
230
+ <BuiCommandNewItem
231
+ v-if="search"
232
+ @create="
233
+ () => {
234
+ setValue([...value, search])
235
+ existingGroups = [...existingGroups, search]
236
+ search = ''
237
+ isGroupsPopoverOpen = false
238
+ }
239
+ "
240
+ >
241
+ <CirclePlusIcon class="mr-2 h-4 w-4" />
242
+ <span>Create "{{ search }}"</span>
243
+ </BuiCommandNewItem>
244
+ </BuiCommandEmpty>
245
+ <BuiFormControl>
246
+ <BuiCommandList>
247
+ <BuiCommandGroup>
248
+ <BuiCommandNewItem
249
+ v-if="search"
250
+ @create="
251
+ () => {
252
+ setValue([...value, search])
253
+ existingGroups = [...existingGroups, search]
254
+ search = ''
255
+ isGroupsPopoverOpen = false
256
+ }
257
+ "
258
+ >
259
+ <CirclePlusIcon class="mr-2 h-4 w-4" />
260
+ <span>Create "{{ search }}"</span>
261
+ </BuiCommandNewItem>
262
+
263
+ <BuiCommandItem
264
+ v-for="element in existingGroups"
265
+ :value="element"
266
+ :key="element"
267
+ @select="
268
+ () => {
269
+ if (value.includes(element)) {
270
+ setValue(value.filter((item: string) => item !== element))
271
+ } else {
272
+ setValue([...value, element])
273
+ }
274
+
275
+ isGroupsPopoverOpen = false
276
+ }
277
+ "
278
+ >
279
+ <CheckIcon
280
+ class="mr-2 h-4 w-4"
281
+ :class="value.includes(element) ? 'opacity-100' : 'opacity-0'"
282
+ />
283
+ {{ element }}
284
+ </BuiCommandItem>
285
+ </BuiCommandGroup>
286
+ </BuiCommandList>
287
+ </BuiFormControl>
288
+ </BuiCommand>
289
+ </BuiPopoverContent>
290
+
291
+ <BuiFormMessage />
292
+ </BuiPopover>
293
+ </BuiFormItem>
294
+ </BuiFormField>
295
+ </BuiForm>
296
+ </div>
297
+ </Variant>
181
298
  </Story>
182
299
  </template>
@@ -80,14 +80,22 @@ const variantOptions = ['default', 'ghost'] as const
80
80
 
81
81
  <Variant title="vertical">
82
82
  <BuiTabs default-value="general" class="flex" orientation="vertical">
83
- <BuiTabsList class="w-40" variant="vertical">
83
+ <BuiTabsList variant="vertical">
84
84
  <BuiTabsTrigger value="general" variant="vertical">General</BuiTabsTrigger>
85
85
  <BuiTabsTrigger value="health" variant="vertical">Health check</BuiTabsTrigger>
86
- <BuiTabsTrigger value="resources" variant="vertical">Resources</BuiTabsTrigger>
86
+ <BuiTabsTrigger value="resources" variant="vertical">
87
+ A Very Very Veeeeeery Long Label For Resources
88
+ </BuiTabsTrigger>
87
89
  </BuiTabsList>
88
- <BuiTabsContent value="general" class="mt-0 flex-grow"> General content </BuiTabsContent>
89
- <BuiTabsContent value="health" class="mt-0"> Health check content </BuiTabsContent>
90
- <BuiTabsContent value="resources" class="mt-0"> Resources content </BuiTabsContent>
90
+ <BuiTabsContent value="general" class="mt-0 flex-grow bg-background p-2">
91
+ General content
92
+ </BuiTabsContent>
93
+ <BuiTabsContent value="health" class="mt-0 bg-background p-2">
94
+ Health check content
95
+ </BuiTabsContent>
96
+ <BuiTabsContent value="resources" class="mt-0 bg-background p-2">
97
+ Resources content
98
+ </BuiTabsContent>
91
99
  </BuiTabs>
92
100
  </Variant>
93
101
  </Story>
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ import BuiButton from '@/components/ui/button/BuiButton.vue'
3
+ import { cn } from '@/lib/utils'
4
+
5
+ const emit = defineEmits(['create'])
6
+
7
+ function handleClick() {
8
+ emit('create')
9
+ }
10
+
11
+ function handleKeydown(event: KeyboardEvent) {
12
+ if (event.key === 'Enter') {
13
+ emit('create')
14
+ }
15
+ }
16
+ </script>
17
+
18
+ <template>
19
+ <BuiButton
20
+ variant="none"
21
+ @click="handleClick"
22
+ @keydown="handleKeydown"
23
+ :class="
24
+ cn(
25
+ 'w-full justify-start px-2 py-1.5 text-left text-primary focus:outline-none',
26
+ 'hover:bg-accent/[0.08] focus:!bg-accent/[0.08]'
27
+ )
28
+ "
29
+ >
30
+ <slot />
31
+ </BuiButton>
32
+ </template>
@@ -7,3 +7,4 @@ export { default as BuiCommandItem } from './BuiCommandItem.vue'
7
7
  export { default as BuiCommandList } from './BuiCommandList.vue'
8
8
  export { default as BuiCommandSeparator } from './BuiCommandSeparator.vue'
9
9
  export { default as BuiCommandShortcut } from './BuiCommandShortcut.vue'
10
+ export { default as BuiCommandNewItem } from './BuiCommandNewItem.vue'
@@ -13,7 +13,7 @@ const props = defineProps<Props>()
13
13
 
14
14
  <template>
15
15
  <TabsTrigger v-bind="props" :class="cn(tabsTriggerVariants({ variant }), props.class ?? '')">
16
- <div class="flex items-center">
16
+ <div class="flex items-center text-left">
17
17
  <slot />
18
18
  </div>
19
19
  </TabsTrigger>
@@ -10,7 +10,8 @@ export const tabsListVariants = cva('inline-flex items-center justify-center rou
10
10
  variant: {
11
11
  default: 'bg-muted text-muted-foreground p-0',
12
12
  ghost: 'bg-background bg-text-foreground',
13
- vertical: 'bg-muted text-muted-foreground p-0 flex flex-col justify-start rounded-none'
13
+ vertical:
14
+ 'w-[17.5rem] shrink-0 bg-tabs-sidebar text-muted-foreground p-0 flex flex-col justify-start rounded-none shadow-tab-sidebar-shadow'
14
15
  }
15
16
  },
16
17
  defaultVariants: {
@@ -19,16 +20,16 @@ export const tabsListVariants = cva('inline-flex items-center justify-center rou
19
20
  })
20
21
 
21
22
  export const tabsTriggerVariants = cva(
22
- 'inline-flex items-center justify-center whitespace-nowrap px-3 py-1 text-sm font-semibold ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground',
23
+ 'inline-flex items-center justify-center px-3 py-1 text-sm font-semibold ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:text-foreground',
23
24
  {
24
25
  variants: {
25
26
  variant: {
26
27
  default:
27
- 'rounded-sm text-foreground opacity-[0.56] hover:opacity-100 data-[state=active]:opacity-100 data-[state=active]:bg-background my-1 [&:first-child]:ml-1 [&:last-child]:mr-1 data-[state=active]:shadow-tab-shadow',
28
+ 'whitespace-nowrap rounded-sm text-foreground opacity-[0.56] hover:opacity-100 data-[state=active]:opacity-100 data-[state=active]:bg-background my-1 [&:first-child]:ml-1 [&:last-child]:mr-1 data-[state=active]:shadow-tab-shadow',
28
29
  ghost:
29
- 'border-transparent text-muted-foreground pb-0 [&_div]:pb-0.5 [&_div]:data-[state=active]:border-b-2 [&_div]:data-[state=active]:border-primary',
30
+ 'whitespace-nowrap border-transparent text-muted-foreground pb-0 [&_div]:pb-0.5 [&_div]:data-[state=active]:border-b-2 [&_div]:data-[state=active]:border-primary',
30
31
  vertical:
31
- 'border-transparent text-primary data-[state=active]:border-primary data-[state=active]:bg-background border-l-2 border-b-0 w-full h-8 justify-start pl-6'
32
+ 'border-transparent text-primary data-[state=active]:border-primary data-[state=active]:bg-background data-[state=active]:shadow-tab-sidebar-shadow border-l-2 border-b-0 w-full h-fit justify-start pl-6'
32
33
  }
33
34
  },
34
35
  defaultVariants: {
@@ -30,6 +30,7 @@ export default {
30
30
  background: 'hsl(var(--background))',
31
31
  foreground: 'hsl(var(--foreground))',
32
32
  sidebar: 'hsl(var(--side-bar))',
33
+ 'tabs-sidebar': 'hsl(var(--tabs-sidebar))',
33
34
  primary: {
34
35
  DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
35
36
  foreground: 'hsl(var(--primary-foreground) / <alpha-value>)'
@@ -92,7 +93,8 @@ export default {
92
93
  level2:
93
94
  '0px 32px 16px 0px hsl(var(--tab-shadow)), 0px 16px 16px 0px hsl(var(--tab-shadow)), 0px 16px 16px 0px hsl(var(--tab-shadow))',
94
95
  level3:
95
- '0px 56px 24px 0px hsl(var(--tab-shadow)), 0px 32px 24px 0px hsl(var(--tab-shadow)), 0px 32px 24px 0px hsl(var(--tab-shadow))'
96
+ '0px 56px 24px 0px hsl(var(--tab-shadow)), 0px 32px 24px 0px hsl(var(--tab-shadow)), 0px 32px 24px 0px hsl(var(--tab-shadow))',
97
+ 'tab-sidebar-shadow': 'inset -8px 0px 16px 0px hsl(var(--tabs-sidebar-shadow))'
96
98
  },
97
99
  keyframes: {
98
100
  'accordion-down': {