@meistrari/tela-build 1.52.0 → 1.52.2

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.
@@ -25,40 +25,50 @@ const resolvedVariant = computed(() =>
25
25
  || 'primary',
26
26
  )
27
27
 
28
+ const slots = useSlots()
29
+
30
+ const hasLeadingIcon = computed(() => !!((props.loading && props.leading) || slots.leading || (props.icon && props.leading)))
31
+ const hasTrailingIcon = computed(() => !!((props.loading && !props.leading) || slots.trailing || (props.icon && !props.leading)))
32
+
28
33
  const sizeStyle = computed(() => (({
29
- sm: 'text-12px leading-16px font-580 tracking-normal px-10px py-4px rounded-9px',
30
- md: 'text-14px leading-18px font-580 -tracking-0.15px px-12px py-7px rounded-11px [&>svg]:text-10px',
31
- lg: 'text-16px leading-20px font-580 -tracking-0.15px px-16px py-10px rounded-13px [&>svg]:text-12px',
34
+ sm: '[--padding-x:10px] text-[12px] leading-[16px] tracking-normal py-[4px] rounded-[9px]',
35
+ md: '[--padding-x:12px] text-[14px] leading-[18px] -tracking-[0.15px] py-[7px] rounded-[11px]',
36
+ lg: '[--padding-x:16px] text-[16px] leading-[20px] -tracking-[0.15px] py-[10px] rounded-[13px]',
32
37
  }) as Record<ButtonSize, string>)[resolvedSize.value] ?? '')
33
38
 
39
+ const paddingStyle = computed(() => [
40
+ hasLeadingIcon.value ? 'pl-[calc(var(--padding-x)_-_2px)]' : 'pl-[var(--padding-x)]',
41
+ hasTrailingIcon.value ? 'pr-[calc(var(--padding-x)_-_2px)]' : 'pr-[var(--padding-x)]',
42
+ ].join(' '))
43
+
34
44
  const variantStyle = computed(() => (({
35
45
  'primary': fold`
36
- bg-neutral-900 text-white
37
- hover:bg-neutral-800
38
- active:bg-neutral-700
46
+ bg-btn-primary text-inverse
47
+ hover:bg-btn-primary-hover
48
+ active:bg-btn-primary-active
39
49
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
40
50
  `,
41
51
  'secondary': fold`
42
- bg-white text-neutral-900 border border-0.5px
52
+ bg-btn-secondary text-contrast border border-0.5px
43
53
  [box-shadow:0_1px_6px_0_rgba(103,127,148,0.05)]
44
- hover:bg-subtle hover:border-strong
45
- active:bg-muted active:border-neutral-400/60
54
+ hover:bg-btn-secondary-hover hover:border-strong
55
+ active:bg-btn-secondary-active active:border-neutral-400/60
46
56
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
47
57
  `,
48
58
  'ghost': fold`
49
- bg-transparent text-gray-900
50
- hover:bg-gray-100
59
+ bg-btn-ghost text-contrast
60
+ hover:bg-btn-ghost-hover
51
61
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
52
62
  `,
53
63
  'disabled': fold`
54
- bg-gray-100 text-gray-400 !cursor-not-allowed
64
+ bg-btn-disabled text-tertiary !cursor-not-allowed
55
65
  `,
56
66
  'loading': fold`
57
- bg-gray-200 text-gray-600
67
+ bg-btn-loading text-neutral-600
58
68
  `,
59
69
  'danger': fold`
60
- bg-red-100 text-red-500
61
- hover:bg-red-100/80
70
+ bg-btn-danger text-red-500
71
+ hover:bg-btn-danger-hover
62
72
  focus:outline-red-600
63
73
  `,
64
74
  'ghost-danger': fold`
@@ -67,14 +77,26 @@ const variantStyle = computed(() => (({
67
77
  focus:outline-red-600
68
78
  `,
69
79
  'dark': fold`
70
- bg-white/4 text-white border-0.5px border-white/16
71
- hover:bg-white/8 active:bg-white/10
80
+ bg-btn-dark text-white border-0.5px border-white/16
81
+ hover:bg-btn-dark-hover active:bg-btn-dark-active
72
82
  focus-visible:ring-0.5px focus-visible:ring-cyan-600
73
83
  `,
74
84
  }) as Record<ButtonVariant | 'disabled' | 'loading', string>)[resolvedVariant.value] ?? '')
75
85
 
86
+ const iconColorStyle = computed(() => (({
87
+ 'primary': 'icon-inverse',
88
+ 'secondary': 'icon-secondary',
89
+ 'ghost': 'icon-secondary',
90
+ 'disabled': 'icon-subtle',
91
+ 'loading': 'icon-tertiary',
92
+ 'danger': 'red-500',
93
+ 'ghost-danger': 'red-700',
94
+ 'dark': 'icon-subtle',
95
+ }) as Record<ButtonVariant | 'disabled' | 'loading', string>)[resolvedVariant.value] ?? '')
96
+
76
97
  const resolvedStyle = computed(() => [
77
98
  sizeStyle.value,
99
+ paddingStyle.value,
78
100
  variantStyle.value,
79
101
  ].join(' '))
80
102
 
@@ -100,19 +122,20 @@ const iconSize = computed(() => {
100
122
  <component
101
123
  :is="tag"
102
124
  :to="to"
103
- :class="resolvedStyle" ease-out duration-40 flex items-center justify-center gap-6px
125
+ :class="cn(
126
+ 'box-border font-540 select-none flex items-center justify-center gap-6px ease-out duration-40',
127
+ disabled ? 'cursor-not-allowed' : 'cursor-pointer',
128
+ resolvedStyle,
129
+ )"
104
130
  :disabled="disabled"
105
- :cursor="disabled ? 'not-allowed' : 'pointer'"
106
131
  :target="target"
107
- border-box
108
- select-none
109
132
  >
110
- <TelaIconSpinner v-if="loading && leading" animate-spin />
133
+ <TelaIconSpinner v-if="loading && leading" class="animate-spin" />
111
134
  <slot v-else-if="$slots.leading" name="leading" />
112
- <TelaIcon v-else-if="icon && leading" :name="icon" :size="iconSize" min-h-14px min-w-14px />
135
+ <TelaIcon v-else-if="icon && leading" :name="icon" :size="iconSize" :color="iconColorStyle" class="min-h-14px min-w-14px" />
113
136
  <slot />
114
- <TelaIconSpinner v-if="loading && !leading" animate-spin ml-0.5 />
137
+ <TelaIconSpinner v-if="loading && !leading" class="animate-spin min-h-14px min-w-14px" />
115
138
  <slot v-else-if="$slots.trailing" name="trailing" />
116
- <TelaIcon v-else-if="icon && !leading" :name="icon" :size="iconSize" />
139
+ <TelaIcon v-else-if="icon && !leading" :name="icon" :size="iconSize" :color="iconColorStyle" class="min-h-14px min-w-14px" />
117
140
  </component>
118
141
  </template>
@@ -15,7 +15,7 @@ const delegatedProps = reactiveOmit(props, 'class')
15
15
  <template>
16
16
  <ComboboxGroup
17
17
  v-bind="delegatedProps"
18
- :class="cn('overflow-hidden py-4px px-4px', props.class)"
18
+ :class="cn('overflow-hidden p-[4px]', props.class)"
19
19
  >
20
20
  <slot />
21
21
  </ComboboxGroup>
@@ -13,7 +13,7 @@ const delegatedProps = reactiveOmit(props, 'class')
13
13
  <template>
14
14
  <ComboboxItemIndicator
15
15
  v-bind="delegatedProps"
16
- :class="cn('text-gray-500', props.class)"
16
+ :class="cn('text-icon-secondary', props.class)"
17
17
  >
18
18
  <slot />
19
19
  </ComboboxItemIndicator>
@@ -8,6 +8,8 @@ import {
8
8
  } from 'reka-ui'
9
9
  import type { ComboboxInputEmits, ComboboxInputProps } from 'reka-ui'
10
10
 
11
+ defineOptions({ inheritAttrs: false })
12
+
11
13
  const props = defineProps<ComboboxInputProps & {
12
14
  class?: HTMLAttributes['class']
13
15
  }>()
@@ -20,10 +22,18 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
20
22
  </script>
21
23
 
22
24
  <template>
23
- <ComboboxInput
24
- v-bind="forwarded"
25
- :class="cn('flex h-10 w-full bg border-b-[0.5px] border-border rounded-xl px-3 py-1 body-14-regular text-primary transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-tertiary focus-visible:bg focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50', props.class)"
26
- >
27
- <slot />
28
- </ComboboxInput>
25
+ <div class="relative">
26
+ <span class="pointer-events-none absolute inset-y-0 start-0 flex items-center justify-center pl-[13px]">
27
+ <TelaIcon name="i-ph-magnifying-glass-bold" size="12px" class="text-tertiary" />
28
+ </span>
29
+ <ComboboxInput
30
+ v-bind="{ ...forwarded, ...$attrs }"
31
+ :class="cn('flex h-[36px] w-full bg border-b-[0.5px] border-border pl-32px pr-3 body-14-regular text-primary transition-colors placeholder:text-tertiary focus-visible:bg focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50', $slots.deleteSearchValue && 'pr-30px', props.class)"
32
+ >
33
+ <slot />
34
+ </ComboboxInput>
35
+ <div v-if="$slots.deleteSearchValue" class="absolute inset-y-0 end-0 flex items-center justify-center px-2.5">
36
+ <slot name="deleteSearchValue" />
37
+ </div>
38
+ </div>
29
39
  </template>
@@ -19,7 +19,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
19
19
  <template>
20
20
  <ComboboxItem
21
21
  v-bind="forwarded"
22
- :class="cn('relative flex cursor-pointer gap-2 select-none justify-between items-center rounded-lg px-2 py-1.5 text-sm font-medium outline-none data-[highlighted]:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', props.class)"
22
+ :class="cn('relative flex cursor-pointer gap-2 select-none justify-between items-center rounded-[12px] pl-[10px] pr-[8px] py-[7px] body-14-medium outline-none data-[highlighted]:bg-muted data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0', props.class)"
23
23
  >
24
24
  <slot />
25
25
  </ComboboxItem>
@@ -15,7 +15,7 @@ const forwarded = useForwardProps(delegatedProps)
15
15
  <template>
16
16
  <ComboboxLabel
17
17
  v-bind="forwarded"
18
- :class="cn('px-2 py-1 body-12-semibold text-secondary', props.class)"
18
+ :class="cn('py-[4px] pl-[10px] body-12-semibold text-secondary', props.class)"
19
19
  >
20
20
  <slot />
21
21
  </ComboboxLabel>
@@ -28,20 +28,10 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
28
28
  </script>
29
29
 
30
30
  <template>
31
- <template v-if="props.disablePortal">
31
+ <ComboboxPortal :disabled="props.disablePortal">
32
32
  <ComboboxContent
33
33
  v-bind="forwarded"
34
- :class="cn('ComboboxContent z-[9999] shadow-lg outline-none rounded-xl bg-white-1000 border-[0.5px] border-gray-200 pointer-events-auto', props.class)"
35
- >
36
- <ComboboxViewport>
37
- <slot />
38
- </ComboboxViewport>
39
- </ComboboxContent>
40
- </template>
41
- <ComboboxPortal v-else>
42
- <ComboboxContent
43
- v-bind="forwarded"
44
- :class="cn('ComboboxContent z-[9999] shadow-lg outline-none rounded-xl bg-white-1000 border-[0.5px] border-gray-200 pointer-events-auto', props.class)"
34
+ :class="cn('ComboboxContent z-[9999] [box-shadow:0_4px_16px_0_rgba(85,91,98,0.14)] overflow-hidden outline-none rounded-[16px] bg border-0.5px border pointer-events-auto', props.class)"
45
35
  >
46
36
  <ComboboxViewport>
47
37
  <slot />
@@ -263,24 +263,14 @@ watch(innerValue, (value) => {
263
263
  @pointer-down-outside="closeOnOutsideClick ? undefined : $event.preventDefault()"
264
264
  @escape-key-down="closeOnEscape ? undefined : $event.preventDefault()"
265
265
  >
266
- <div class="relative">
267
- <span class="absolute inset-y-0 start-0 flex items-center justify-center px-2.5">
268
- <TelaIcon name="i-ph-magnifying-glass" size="12px" class="text-gray-400" />
269
- </span>
270
- <ComboboxInput
271
- v-model="search" class="pl-7 rounded-b-none"
272
- :placeholder="t('models.labels.search')"
273
- />
274
- <div v-if="search.length > 0" class="absolute inset-y-0 end-0 flex items-center justify-center px-2.5">
275
- <button class="flex items-center justify-center" @click="handleDeleteSearchValue">
276
- <TelaIcon name="i-ph-x-circle" size="16px" class="text-gray-400" />
277
- </button>
278
- </div>
279
- </div>
266
+ <ComboboxInput
267
+ v-model="search"
268
+ :placeholder="t('models.labels.search')"
269
+ />
280
270
  <Tabs v-model="selectedTab" :default-value="selectedTab">
281
271
  <TabsList class="sticky z-10 top-0 shrink-0 flex gap-2 w-full">
282
272
  <TabsIndicator />
283
- <TabsTrigger v-for="tab in tabs" :key="tab.tab" :value="tab.tab">
273
+ <TabsTrigger v-for="tab in tabs" :key="tab.tab" :value="tab.tab" size="sm">
284
274
  {{ tab.label }}
285
275
  </TabsTrigger>
286
276
  </TabsList>
@@ -315,7 +305,7 @@ watch(innerValue, (value) => {
315
305
  </TelaButton>
316
306
  </ComboboxEmpty>
317
307
  <template v-for="group in groupedOptions" :key="group.heading">
318
- <ComboboxGroup :heading="group.heading" class="py-2">
308
+ <ComboboxGroup :heading="group.heading">
319
309
  <ComboboxLabel v-if="!search.trim()" class="py-0 tracking-normal">
320
310
  {{ group.heading }}
321
311
  </ComboboxLabel>
@@ -1,12 +1,30 @@
1
1
  <script setup lang="ts">
2
+ import type { HTMLAttributes } from 'vue'
3
+ import { reactiveOmit } from '@vueuse/core'
2
4
  import { ComboboxTrigger } from 'reka-ui'
3
5
  import type { ComboboxTriggerProps } from 'reka-ui'
4
6
 
5
- const props = defineProps<ComboboxTriggerProps>()
7
+ const props = defineProps<ComboboxTriggerProps & { class?: HTMLAttributes['class'] }>()
8
+
9
+ const delegatedProps = reactiveOmit(props, 'class')
6
10
  </script>
7
11
 
8
12
  <template>
9
- <ComboboxTrigger v-bind="props">
13
+ <ComboboxTrigger
14
+ v-bind="delegatedProps"
15
+ :class="cn(
16
+ 'group select-none flex items-center justify-between body-14-semibold gap-3 rounded-10px pl-11px pr-10px py-7px bg-white border-0.5px border transition hover:border-strong hover:bg-subtle',
17
+ 'data-[state=open]:text-secondary data-[state=open]:border-strong data-[state=open]:bg-subtle',
18
+ '[box-shadow:0_1px_6px_0_rgba(103,127,148,0.05)]',
19
+ props.class,
20
+ )"
21
+ >
10
22
  <slot />
23
+ <TelaIcon
24
+ name="i-ph-caret-down-bold"
25
+ size="15px"
26
+ color="icon-secondary"
27
+ class="shrink-0 transition-transform ease-out duration-150 group-data-[state=open]:rotate-180"
28
+ />
11
29
  </ComboboxTrigger>
12
30
  </template>
@@ -93,7 +93,7 @@ const meta: Meta<typeof Combobox> = {
93
93
 
94
94
  export default meta
95
95
 
96
- export const Default: StoryObj<{ options: Array<{ value: string; label: string }> }> = {
96
+ export const Default: StoryObj<{ options: Array<{ value: string, label: string }> }> = {
97
97
  render: args => ({
98
98
  components: { ComboboxRoot, ComboboxAnchor, ComboboxTrigger, TelaIcon, TelaButton, ComboboxInput, ComboboxList, ComboboxEmpty, ComboboxGroup, ComboboxItem, ComboboxItemIndicator, ComboboxLabel },
99
99
  setup() {
@@ -105,14 +105,14 @@ export const Default: StoryObj<{ options: Array<{ value: string; label: string }
105
105
  return selectedValue.value
106
106
  }
107
107
 
108
- return args.options.find((option: any) => option.value === selectedValue.value)
108
+ return args.options.find(option => option.value === selectedValue.value)
109
109
  })
110
110
 
111
111
  const filteredOptions = computed(() => {
112
112
  if (!search.value.trim()) {
113
113
  return args.options
114
114
  }
115
- return args.options.filter((option: any) =>
115
+ return args.options.filter(option =>
116
116
  option.label.toLowerCase().includes(search.value.toLowerCase()),
117
117
  )
118
118
  })