@indielayer/ui 1.12.0 → 1.13.0

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.
Files changed (44) hide show
  1. package/docs/pages/component/datepicker/usage.vue +1 -0
  2. package/docs/pages/component/select/multiple.vue +4 -2
  3. package/docs/pages/component/select/usage.vue +1 -0
  4. package/docs/pages/component/table/usage.vue +1 -1
  5. package/docs/pages/component/tabs/usage.vue +1 -0
  6. package/docs/pages/component/upload/usage.vue +28 -18
  7. package/lib/components/datepicker/Datepicker.vue.d.ts +12 -12
  8. package/lib/components/datepicker/Datepicker.vue.js +6 -6
  9. package/lib/components/modal/Modal.vue.js +1 -4
  10. package/lib/components/select/Select.vue.d.ts +5 -1
  11. package/lib/components/select/Select.vue.js +261 -244
  12. package/lib/components/select/theme/Select.base.theme.js +11 -10
  13. package/lib/components/tab/theme/Tab.base.theme.js +2 -2
  14. package/lib/components/tab/theme/TabGroup.base.theme.js +1 -1
  15. package/lib/components/table/Table.vue.d.ts +1 -0
  16. package/lib/components/table/Table.vue.js +51 -50
  17. package/lib/components/table/TableHeader.vue.d.ts +3 -0
  18. package/lib/components/table/TableHeader.vue.js +58 -49
  19. package/lib/components/table/theme/TableHead.base.theme.js +1 -1
  20. package/lib/components/table/theme/TableHeader.base.theme.js +3 -3
  21. package/lib/components/table/theme/TableRow.base.theme.js +1 -1
  22. package/lib/components/upload/Upload.vue.d.ts +13 -0
  23. package/lib/components/upload/Upload.vue.js +100 -94
  24. package/lib/index.js +1 -1
  25. package/lib/index.umd.js +4 -4
  26. package/lib/node_modules/.pnpm/@vuepic_vue-datepicker@11.0.2_vue@3.5.10_typescript@5.2.2_/node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.js +5210 -0
  27. package/lib/version.d.ts +1 -1
  28. package/lib/version.js +1 -1
  29. package/package.json +2 -2
  30. package/src/components/datepicker/Datepicker.vue +1 -1
  31. package/src/components/modal/Modal.vue +2 -0
  32. package/src/components/select/Select.vue +26 -7
  33. package/src/components/select/theme/Select.base.theme.ts +17 -6
  34. package/src/components/tab/theme/Tab.base.theme.ts +3 -2
  35. package/src/components/tab/theme/TabGroup.base.theme.ts +1 -1
  36. package/src/components/table/Table.vue +2 -0
  37. package/src/components/table/TableHeader.vue +36 -28
  38. package/src/components/table/theme/TableHead.base.theme.ts +1 -1
  39. package/src/components/table/theme/TableHeader.base.theme.ts +1 -1
  40. package/src/components/table/theme/TableRow.base.theme.ts +1 -1
  41. package/src/components/upload/Upload.vue +15 -6
  42. package/src/version.ts +1 -1
  43. package/lib/node_modules/.pnpm/@vuepic_vue-datepicker@11.0.1_vue@3.5.10_typescript@5.2.2_/node_modules/@vuepic/vue-datepicker/dist/vue-datepicker.js +0 -5199
  44. /package/lib/node_modules/.pnpm/{@vuepic_vue-datepicker@11.0.1_vue@3.5.10_typescript@5.2.2_ → @vuepic_vue-datepicker@11.0.2_vue@3.5.10_typescript@5.2.2_}/node_modules/@vuepic/vue-datepicker/dist/main.css.js +0 -0
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "1.12.0";
1
+ declare const _default: "1.13.0";
2
2
  export default _default;
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const e = "1.12.0";
1
+ const e = "1.13.0";
2
2
  export {
3
3
  e as default
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@indielayer/ui",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Indielayer UI Components with Tailwind CSS build for Vue 3",
5
5
  "author": {
6
6
  "name": "João Teixeira",
@@ -57,7 +57,7 @@
57
57
  "@vitejs/plugin-vue-jsx": "^3.0.1",
58
58
  "@vue/test-utils": "^2.4.0",
59
59
  "@vue/tsconfig": "^0.4.0",
60
- "@vuepic/vue-datepicker": "^11.0.1",
60
+ "@vuepic/vue-datepicker": "^11.0.2",
61
61
  "@vueuse/core": "^11.1.0",
62
62
  "autoprefixer": "^10.4.0",
63
63
  "c8": "^7.12.0",
@@ -335,7 +335,7 @@ const { styles, classes, className } = useTheme('Datepicker', {}, props)
335
335
  box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
336
336
  }
337
337
 
338
- .dp__clear_icon {
338
+ .dp--clear-btn {
339
339
  top: 2.75rem !important;
340
340
  }
341
341
 
@@ -143,6 +143,8 @@ const shouldIgnoreEvent = (event: KeyboardEvent) => {
143
143
  return Array.from(window.document.querySelectorAll(target))
144
144
  .some((el) => el === event.target || event.composedPath().includes(el))
145
145
  }
146
+
147
+ return false
146
148
  })
147
149
  }
148
150
 
@@ -12,6 +12,7 @@ const selectProps = {
12
12
  flat: Boolean,
13
13
  native: Boolean,
14
14
  filterable: Boolean,
15
+ clearable: Boolean,
15
16
  filterPlaceholder: {
16
17
  type: String,
17
18
  default: 'Filter by...',
@@ -42,7 +43,7 @@ export type SelectOption = {
42
43
 
43
44
  export type SelectProps = ExtractPublicPropTypes<typeof selectProps>
44
45
 
45
- type InternalClasses = 'wrapper' | 'box' | 'truncateCounter' | 'content' | 'search' | 'contentBody' | 'iconWrapper' | 'icon'
46
+ type InternalClasses = 'wrapper' | 'box' | 'truncateCounter' | 'content' | 'search' | 'contentBody' | 'iconWrapper' | 'clearButton' | 'icon'
46
47
  type InternalExtraData = { errorInternal: Ref<boolean>; }
47
48
  export interface SelectTheme extends ThemeComponent<SelectProps, InternalClasses, InternalExtraData> {}
48
49
 
@@ -63,7 +64,7 @@ import { useInputtable } from '../../composables/useInputtable'
63
64
  import { useInteractive } from '../../composables/useInteractive'
64
65
  import { useTheme, type ThemeComponent } from '../../composables/useTheme'
65
66
  import { useVirtualList } from '../../composables/useVirtualList'
66
- import { checkIcon, selectIcon } from '../../common/icons'
67
+ import { checkIcon, selectIcon, closeIcon } from '../../common/icons'
67
68
 
68
69
  import XLabel from '../label/Label.vue'
69
70
  import XTag from '../tag/Tag.vue'
@@ -85,12 +86,14 @@ const elRef = ref<HTMLElement | null>(null)
85
86
  const labelRef = ref<InstanceType<typeof XLabel> | null>(null)
86
87
  const itemsRef = ref<InstanceType<typeof XMenuItem>[] | null>(null)
87
88
  const popoverRef = ref<InstanceType<typeof XPopover> | null>(null)
89
+ const hiddenTagsCounterRef = ref<HTMLElement | null>(null)
88
90
  const selectedIndex = ref<number | undefined>()
89
91
 
90
92
  const filter = defineModel('filter', { default : '' })
91
93
  const filterRef = ref<InstanceType<typeof XInput> | null>(null)
92
94
 
93
95
  const isDisabled = computed(() => props.disabled || props.loading || props.readonly)
96
+ const isClearIconVisible = computed(() => !props.loading && !props.readonly && !props.disabled && props.clearable && !isEmpty(selected.value))
94
97
 
95
98
  const selected = computed<any | any[]>({
96
99
  get() {
@@ -435,8 +438,10 @@ function calcMaxTags() {
435
438
 
436
439
  totalWidth += tag.offsetWidth
437
440
 
438
- if (totalWidth < maxWidth) tagsCount++
439
- else tag.style.display = 'none'
441
+ if (i > 0) {
442
+ if (totalWidth < maxWidth) tagsCount++
443
+ else tag.style.display = 'none'
444
+ }
440
445
  }
441
446
 
442
447
  return tagsCount
@@ -504,12 +509,13 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
504
509
  }"
505
510
  >
506
511
  <x-tag
507
- v-for="value in selected"
512
+ v-for="(value, valueIndex) in selected"
508
513
  :key="value"
509
514
  size="xs"
510
515
  removable
511
516
  :outlined="!(isDisabled || options?.find((i) => i.value === value)?.disabled)"
512
517
  :disabled="isDisabled || options?.find((i) => i.value === value)?.disabled"
518
+ :style="{ 'max-width': valueIndex === 0 && hiddenTagsCounterRef ? `calc(100% - ${hiddenTagsCounterRef.offsetWidth + 6 + 'px'})` : undefined }"
513
519
  @remove="(e: Event) => { handleRemove(e, value) }"
514
520
  >
515
521
  <template #prefix>
@@ -520,6 +526,7 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
520
526
 
521
527
  <div
522
528
  v-if="showCountTag"
529
+ ref="hiddenTagsCounterRef"
523
530
  :class="classes.truncateCounter"
524
531
  @click.stop="multipleHiddenRef?.toggle()"
525
532
  >+{{ hiddenTags }}</div>
@@ -638,17 +645,29 @@ defineExpose({ focus, blur, reset, validate, setError, filterRef })
638
645
  </template>
639
646
  </select>
640
647
 
648
+ <button
649
+ v-if="isClearIconVisible"
650
+ type="button"
651
+ aria-label="Clean value"
652
+ :class="classes.clearButton"
653
+ @click="reset"
654
+ >
655
+ <x-icon
656
+ :icon="closeIcon"
657
+ :class="[classes.icon, 'cursor-pointer']"
658
+ />
659
+ </button>
660
+
641
661
  <div v-if="!$slots.input" :class="classes.iconWrapper">
642
662
  <x-spinner v-if="loading" :size="size" />
643
663
  <slot v-else name="icon">
644
664
  <x-icon
645
665
  :icon="selectIcon"
646
- :class="[classes.icon]"
666
+ :class="classes.icon"
647
667
  />
648
668
  </slot>
649
669
  </div>
650
670
  </div>
651
-
652
671
  <x-input-footer v-if="!hideFooterInternal" :error="errorInternal" :helper="helper"/>
653
672
  </x-label>
654
673
  </template>
@@ -5,7 +5,7 @@ const theme: SelectTheme = {
5
5
  wrapper: '',
6
6
 
7
7
  box: ({ props, data }) => {
8
- const classes = ['w-full border border-secondary-300 dark:border-secondary-700 pr-8 outline-transparent outline outline-2 outline-offset-[-1px] transition-all duration-150 ease-in-out rounded-md shadow-sm']
8
+ const classes = ['w-full border border-secondary-300 dark:border-secondary-700 outline-transparent outline outline-2 outline-offset-[-1px] transition-all duration-150 ease-in-out rounded-md shadow-sm']
9
9
 
10
10
  if (!data.errorInternal && !props.disabled) classes.push('hover:border-secondary-400 dark:hover:border-secondary-500')
11
11
 
@@ -24,6 +24,16 @@ const theme: SelectTheme = {
24
24
  classes.push('group-focus:outline-[color:var(--x-select-border)]')
25
25
  }
26
26
 
27
+ if (!props.multiple && !props.multipleCheckbox) {
28
+ classes.push('truncate')
29
+ }
30
+
31
+ if (props.clearable) {
32
+ classes.push('pr-14')
33
+ } else {
34
+ classes.push('pr-8')
35
+ }
36
+
27
37
  return classes
28
38
  },
29
39
 
@@ -35,15 +45,16 @@ const theme: SelectTheme = {
35
45
 
36
46
  contentBody: 'overflow-y-auto max-h-64 min-w-[280px]',
37
47
 
38
- iconWrapper: 'pointer-events-none absolute inset-y-0 right-0 flex items-center px-2',
48
+ iconWrapper: 'absolute inset-y-0 right-0 flex items-center px-2 pointer-events-none',
49
+
50
+ clearButton: 'absolute top-1/2 -translate-y-1/2 right-5 flex items-center p-2',
39
51
 
40
52
  icon: ({ props }) => {
41
- const classes = ['']
53
+ const classes = []
42
54
 
43
55
  if (props.size === 'sm' || props.size === 'xs') classes.push('h-3 w-3')
44
- else if (props.size === 'lg') classes.push('h-6 w-6')
45
- else if (props.size === 'xl') classes.push('h-7 w-7')
46
- else classes.push('h-5 w-5')
56
+ else if (props.size === 'lg' || props.size === 'xl') classes.push('h-5 w-5')
57
+ else classes.push('h-4 w-4')
47
58
 
48
59
  if (props.disabled) classes.push('text-secondary-300 dark:text-secondary-500')
49
60
  else classes.push('text-secondary-500 dark:text-secondary-400')
@@ -3,9 +3,10 @@ import type { TabTheme } from '../Tab.vue'
3
3
  const theme: TabTheme = {
4
4
  classes: {
5
5
  wrapper: ({ props, data }) => {
6
- const c = ['py-2 transition-colors duration-150 ease-in-out whitespace-nowrap text-center']
6
+ const c = ['transition-colors duration-150 ease-in-out whitespace-nowrap text-center']
7
7
 
8
- if (data.variant === 'block') c.push('px-8')
8
+ if (data.variant === 'line') c.push('py-2')
9
+ if (data.variant === 'block') c.push('py-1.5 px-8')
9
10
 
10
11
  if (props.size === 'xs') c.push('text-xs')
11
12
  else if (props.size === 'sm') c.push('text-sm')
@@ -8,7 +8,7 @@ const theme: TabGroupTheme = {
8
8
  const c = ['']
9
9
 
10
10
  if (!props.fullWidth) c.push('!w-fit')
11
- if (props.variant === 'block') c.push('rounded-md')
11
+ if (props.variant === 'block') c.push('rounded-lg')
12
12
  if (props.variant === 'block' && !props.ghost) c.push('bg-secondary-100 dark:bg-secondary-800 p-1')
13
13
 
14
14
  return c
@@ -55,6 +55,7 @@ export type TableHeader = {
55
55
  width?: string | number;
56
56
  truncate?: boolean;
57
57
  skeletonShape?: SkeletonShape;
58
+ tooltip?: string;
58
59
  }
59
60
 
60
61
  export type TableProps = ExtractPublicPropTypes<typeof tableProps>
@@ -208,6 +209,7 @@ const { styles, classes, className } = useTheme('Table', {}, props)
208
209
  :sort="getSort(header.value, sort)"
209
210
  :sortable="header.sortable"
210
211
  :width="header.width"
212
+ :tooltip="header.tooltip"
211
213
  @click="header.sortable ? sortHeader(header) : null"
212
214
  >
213
215
  <slot :name="`header-${header.value}`" :header="header">
@@ -15,6 +15,7 @@ const tableHeaderProps = {
15
15
  default: 'left',
16
16
  validator: (value: string) => validators.textAlign.includes(value as any),
17
17
  },
18
+ tooltip: String,
18
19
  }
19
20
 
20
21
  export type TableHeaderSort = typeof validators.sort[number]
@@ -30,6 +31,7 @@ export default { name: 'XTableHeader', validators }
30
31
  <script setup lang="ts">
31
32
  import type { ExtractPublicPropTypes, PropType } from 'vue'
32
33
  import { useTheme, type ThemeComponent } from '../../composables/useTheme'
34
+ import XToggleTip from '../tooltip/ToggleTip.vue'
33
35
 
34
36
  const props = defineProps(tableHeaderProps)
35
37
 
@@ -38,36 +40,42 @@ const { styles, classes, className } = useTheme('TableHeader', {}, props)
38
40
 
39
41
  <template>
40
42
  <th :style="styles" :class="[className, classes.th, 'group/th']">
41
- <slot></slot>
43
+ <div class="flex items-center gap-1 select-none">
44
+ <slot></slot>
42
45
 
43
- <svg
44
- v-if="sortable"
45
- :class="[
46
- classes.sortIcon,
47
- [sort && [1, -1].includes(sort) ? '' : 'invisible group-hover/th:visible']
48
- ]"
49
- width="24"
50
- height="24"
51
- viewBox="0 0 24 24"
52
- stroke="currentColor"
53
- stroke-linejoin="round"
54
- stroke-linecap="round"
55
- fill="none"
56
- role="presentation"
57
- >
58
- <template v-if="sort === -1">
59
- <line x1="12" y1="5" x2="12" y2="19"/>
60
- <polyline points="19 12 12 19 5 12" />
61
- </template>
46
+ <x-toggle-tip v-if="tooltip" :content="tooltip"/>
62
47
 
63
- <template v-else-if="sort === 1">
64
- <line x1="12" y1="19" x2="12" y2="5"/>
65
- <polyline points="5 12 12 5 19 12" />
66
- </template>
48
+ <svg
49
+ v-if="sortable"
50
+ class="shrink-0"
51
+ :class="[
52
+ classes.sortIcon,
53
+ [sort && [1, -1].includes(sort) ? '' : 'invisible group-hover/th:visible'],
54
+ [sort !== -1 && sort !== 1 ? 'text-secondary-400' : 'text-primary-700']
55
+ ]"
56
+ width="24"
57
+ height="24"
58
+ viewBox="0 0 24 24"
59
+ stroke="currentColor"
60
+ stroke-linejoin="round"
61
+ stroke-linecap="round"
62
+ fill="none"
63
+ role="presentation"
64
+ >
65
+ <template v-if="sort === -1">
66
+ <line x1="12" y1="5" x2="12" y2="19"/>
67
+ <polyline points="19 12 12 19 5 12" />
68
+ </template>
67
69
 
68
- <template v-else>
69
- <path d="m3 9l4-4l4 4M7 5v14m14-4l-4 4l-4-4m4 4V5"/>
70
- </template>
71
- </svg>
70
+ <template v-else-if="sort === 1">
71
+ <line x1="12" y1="19" x2="12" y2="5"/>
72
+ <polyline points="5 12 12 5 19 12" />
73
+ </template>
74
+
75
+ <template v-else>
76
+ <path d="m3 9l4-4l4 4M7 5v14m14-4l-4 4l-4-4m4 4V5"/>
77
+ </template>
78
+ </svg>
79
+ </div>
72
80
  </th>
73
81
  </template>
@@ -3,7 +3,7 @@ import type { TableHeadTheme } from '../TableHead.vue'
3
3
  const theme: TableHeadTheme = {
4
4
  classes: {
5
5
  thead: ({ props }) => {
6
- const classes = ['align-bottom bg-secondary-50 dark:bg-secondary-700']
6
+ const classes = ['align-bottom bg-secondary-100 dark:bg-secondary-700']
7
7
 
8
8
  if (props.stickyHeader) classes.push('sticky top-0 z-10')
9
9
 
@@ -16,7 +16,7 @@ const theme: TableHeaderTheme = {
16
16
  },
17
17
 
18
18
  sortIcon: ({ props }) => {
19
- const classes = ['absolute stroke-2 w-3 h-3 top-3.5 right-0.5']
19
+ const classes = ['stroke-2 w-3 h-3']
20
20
 
21
21
  return classes
22
22
  },
@@ -6,7 +6,7 @@ const theme: TableRowTheme = {
6
6
  const classes = []
7
7
 
8
8
  if (props.selected) {
9
- classes.push('shadow-[inset_3px_0] shadow-primary-500')
9
+ classes.push('shadow-[inset_2px_0] shadow-primary-500')
10
10
  }
11
11
 
12
12
  if (props.striped) {
@@ -25,6 +25,10 @@ const uploadProps = {
25
25
  default: 'POST',
26
26
  },
27
27
  withCredentials: Boolean,
28
+ fileFormDataName: {
29
+ type: String,
30
+ default: 'file',
31
+ },
28
32
  }
29
33
 
30
34
  export type UploadFile = {
@@ -65,7 +69,7 @@ const props = defineProps(uploadProps)
65
69
 
66
70
  const emit = defineEmits([...useInputtable.emits(), 'upload', 'remove'])
67
71
 
68
- const elRef = ref<HTMLElement | null>(null)
72
+ const elRef = ref<HTMLInputElement | null>(null)
69
73
 
70
74
  const isUploadMode = computed(() => !!props.action)
71
75
 
@@ -202,7 +206,7 @@ async function uploadFileRequest(uploadFile: UploadFile) {
202
206
  const xhr = new XMLHttpRequest()
203
207
  const formData = new FormData()
204
208
 
205
- formData.append('file', uploadFile.file)
209
+ formData.append(props.fileFormDataName, uploadFile.file)
206
210
 
207
211
  xhr.open(props.method, props.action, true)
208
212
 
@@ -219,14 +223,12 @@ async function uploadFileRequest(uploadFile: UploadFile) {
219
223
  const percentComplete = (event.loaded / event.total) * 100
220
224
 
221
225
  uploadFile.progress = percentComplete
222
-
223
- console.log(`Upload progress: ${percentComplete}%`)
224
226
  }
225
227
  })
226
228
 
227
229
  // Event listener when upload is complete
228
230
  xhr.addEventListener('load', () => {
229
- if (xhr.status === 200) {
231
+ if (xhr.status >= 200 && xhr.status < 300) {
230
232
  uploadFile.completed = true
231
233
  try {
232
234
  uploadFile.response = JSON.parse(xhr.responseText)
@@ -267,13 +269,20 @@ async function upload() {
267
269
  validate(modelValue.value)
268
270
  }
269
271
 
272
+ function reset() {
273
+ errorInternal.value = ''
274
+ isFirstValidation.value = true
275
+ modelValue.value = []
276
+
277
+ if (elRef.value) elRef.value.value = ''
278
+ }
279
+
270
280
  const {
271
281
  errorInternal,
272
282
  hideFooterInternal,
273
283
  isInsideForm,
274
284
  inputListeners,
275
285
  isFirstValidation,
276
- reset,
277
286
  validate,
278
287
  setError,
279
288
  } = useInputtable(props, { focus, emit })
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '1.12.0'
1
+ export default '1.13.0'