@nexxtmove/ui 0.1.37 → 0.1.38

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@nexxtmove/ui",
3
3
  "type": "module",
4
- "version": "0.1.37",
4
+ "version": "0.1.38",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -140,7 +140,7 @@ const transitionClasses = computed(() => {
140
140
  <div
141
141
  v-if="isOpen"
142
142
  role="menu"
143
- class="absolute top-full z-10 mt-2.5 min-w-full rounded-md border border-gray-200 bg-white"
143
+ class="absolute top-full z-10 mt-2.5 min-w-full rounded-md border border-gray-200 bg-white whitespace-nowrap"
144
144
  :class="placement === 'right' ? 'right-0' : 'left-0'"
145
145
  >
146
146
  <button
@@ -1,9 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { ref, computed } from 'vue'
3
2
  import Icon from '../Icon/Icon.vue'
4
3
 
5
4
  const generateId = () => 'input-field-' + Math.random().toString(36).slice(2, 10)
6
- const inputId = ref(generateId())
5
+ const inputId = generateId()
7
6
 
8
7
  defineOptions({
9
8
  name: 'NexxtInputField',
@@ -14,71 +13,57 @@ interface NexxtInputFieldProps {
14
13
  label?: string
15
14
  error?: boolean
16
15
  errorMessage?: string
17
- focused?: boolean
18
16
  leftIcon?: InstanceType<typeof Icon>['name']
19
17
  rightIcon?: InstanceType<typeof Icon>['name']
20
18
  }
21
19
 
22
- const props = defineProps<NexxtInputFieldProps>()
20
+ defineProps<NexxtInputFieldProps>()
23
21
 
24
- // ✅ Correct usage
25
22
  const modelValue = defineModel<string>()
26
-
27
- const isFocused = ref(false)
28
-
29
- const wrapperClasses = computed(() => {
30
- if (props.error) {
31
- return 'border-brick-400'
32
- }
33
- if (props.focused || isFocused.value) {
34
- return 'border-cornflower-blue-500'
35
- }
36
- return 'border-gray-200'
37
- })
38
-
39
- const onFocus = () => {
40
- isFocused.value = true
41
- }
42
-
43
- const onBlur = () => {
44
- isFocused.value = false
45
- }
46
23
  </script>
47
24
 
48
25
  <template>
49
- <div class="flex w-full flex-col gap-1">
26
+ <div class="inline-flex flex-col gap-1">
50
27
  <label
51
- v-if="props.label"
28
+ v-if="label"
52
29
  class="text-color-gray-700 justify-start body-normal text-sm"
53
30
  :for="inputId"
54
31
  >
55
- {{ props.label }}
32
+ {{ label }}
56
33
  </label>
57
34
 
58
35
  <div
59
- class="flex items-center gap-2 rounded-lg border px-3 py-2 transition-colors"
60
- :class="wrapperClasses"
36
+ class="flex items-center gap-2 rounded-lg px-3 ring transition-colors ease-out focus-within:ring-cornflower-blue-500"
37
+ :class="error ? 'ring-brick-400' : 'ring-gray-200'"
61
38
  >
62
- <span v-if="props.leftIcon" class="flex items-center">
63
- <Icon :name="props.leftIcon" />
39
+ <span v-if="leftIcon" class="flex items-center">
40
+ <Icon :name="leftIcon" />
64
41
  </span>
65
42
 
66
43
  <input
67
44
  :id="inputId"
68
45
  v-model="modelValue"
69
- :placeholder="props.placeholder"
70
- @focus="onFocus"
71
- @blur="onBlur"
72
- class="w-full bg-transparent text-sm text-gray-900 placeholder-gray-400 focus:outline-none"
46
+ :placeholder="placeholder"
47
+ v-bind="$attrs"
48
+ class="h-10 min-w-60 bg-transparent text-sm text-gray-900 placeholder-gray-400 focus:outline-none"
73
49
  />
74
50
 
75
- <span v-if="props.rightIcon" class="flex items-center">
76
- <Icon :name="props.rightIcon" />
51
+ <span v-if="rightIcon" class="flex items-center">
52
+ <Icon :name="rightIcon" />
77
53
  </span>
78
54
  </div>
79
55
 
80
- <p v-if="props.errorMessage" class="text-sm text-brick-400">
81
- {{ props.errorMessage }}
82
- </p>
56
+ <Transition
57
+ enter-active-class="transition-all duration-200 ease-out"
58
+ enter-from-class="opacity-0 -translate-y-1"
59
+ enter-to-class="opacity-100 translate-y-0"
60
+ leave-active-class="transition-all duration-150 ease-in"
61
+ leave-from-class="opacity-100 translate-y-0"
62
+ leave-to-class="opacity-0 -translate-y-1"
63
+ >
64
+ <p v-if="error && errorMessage" class="text-sm text-brick-400">
65
+ {{ errorMessage }}
66
+ </p>
67
+ </Transition>
83
68
  </div>
84
69
  </template>
@@ -1,4 +1,4 @@
1
- <script lang="ts" setup generic="TKey extends string">
1
+ <script lang="ts" setup generic="TKey extends string, TRow extends Record<TKey, unknown>">
2
2
  import { computed, ref } from 'vue'
3
3
  import Icon from '../Icon/Icon.vue'
4
4
  import Checkbox from '../Checkbox/Checkbox.vue'
@@ -10,9 +10,12 @@ export interface TableColumn<K extends string = string> {
10
10
  width?: string
11
11
  }
12
12
 
13
- export interface NexxtTableProps<TKey extends string = string> {
13
+ export interface NexxtTableProps<
14
+ TKey extends string = string,
15
+ TRow extends Record<TKey, unknown> = Record<TKey, unknown>,
16
+ > {
14
17
  columns: TableColumn<TKey>[]
15
- rows: Record<TKey, unknown>[]
18
+ rows: TRow[]
16
19
  selectable?: boolean
17
20
  }
18
21
 
@@ -20,12 +23,12 @@ const INTERACTIVE = ['a', 'button', 'input', 'select', 'textarea', 'label']
20
23
 
21
24
  defineOptions({ name: 'NexxtTable' })
22
25
 
23
- const props = defineProps<NexxtTableProps<TKey>>()
26
+ const props = defineProps<NexxtTableProps<TKey, TRow>>()
24
27
  const emit = defineEmits<{
25
- 'row-click': [row: Record<TKey, unknown>]
28
+ 'row-click': [row: TRow]
26
29
  }>()
27
30
 
28
- const selected = defineModel<Record<TKey, unknown>[]>({ default: () => [] })
31
+ const selected = defineModel<TRow[]>({ default: () => [] })
29
32
  const sortKey = defineModel<TKey>('sortKey')
30
33
  const sortDirection = defineModel<'asc' | 'desc'>('sortDirection', { default: 'asc' })
31
34
 
@@ -38,13 +41,13 @@ const someSelected = computed(
38
41
  () => selected.value.length > 0 && selected.value.length < props.rows.length,
39
42
  )
40
43
 
41
- const isRowSelected = (row: Record<TKey, unknown>) => selected.value.includes(row)
44
+ const isRowSelected = (row: TRow) => selected.value.includes(row)
42
45
 
43
46
  const toggleAll = () => {
44
47
  selected.value = allSelected.value ? [] : [...props.rows]
45
48
  }
46
49
 
47
- const toggleRow = (row: Record<TKey, unknown>) => {
50
+ const toggleRow = (row: TRow) => {
48
51
  selected.value = isRowSelected(row)
49
52
  ? selected.value.filter((r) => r !== row)
50
53
  : [...selected.value, row]
@@ -84,6 +87,8 @@ const getRowRoundedClass = (rowIndex: number) => {
84
87
  return ''
85
88
  }
86
89
 
90
+ const rowValue = (row: TRow, key: TKey) => (row as Record<TKey, unknown>)[key]
91
+
87
92
  const handleRowKeydown = (event: KeyboardEvent, rowIndex: number) => {
88
93
  if (event.key === 'ArrowDown') {
89
94
  event.preventDefault()
@@ -210,8 +215,8 @@ const handleRowKeydown = (event: KeyboardEvent, rowIndex: number) => {
210
215
  role="gridcell"
211
216
  :aria-colindex="colIndex + (selectable ? 2 : 1)"
212
217
  >
213
- <slot :name="`cell-${column.key}`" :row="row" :value="row[column.key]">
214
- {{ row[column.key] }}
218
+ <slot :name="`cell-${column.key}`" :row="row" :value="rowValue(row, column.key)">
219
+ {{ rowValue(row, column.key) }}
215
220
  </slot>
216
221
  </div>
217
222
  </div>