@indielayer/ui 1.10.5 → 1.12.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.
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "1.10.5";
1
+ declare const _default: "1.12.0";
2
2
  export default _default;
package/lib/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const e = "1.10.5";
1
+ const e = "1.12.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.10.5",
3
+ "version": "1.12.0",
4
4
  "description": "Indielayer UI Components with Tailwind CSS build for Vue 3",
5
5
  "author": {
6
6
  "name": "João Teixeira",
@@ -34,6 +34,7 @@ const menuItemProps = {
34
34
  minimal: Boolean,
35
35
  prefix: String,
36
36
  suffix: String,
37
+ checkbox: Boolean,
37
38
  }
38
39
 
39
40
  export type MenuItemProps = ExtractPublicPropTypes<typeof menuItemProps>
@@ -64,7 +65,7 @@ import type { MenuArrayItem } from './Menu.vue'
64
65
 
65
66
  const props = defineProps(menuItemProps)
66
67
 
67
- const emit = defineEmits(['active', 'click'])
68
+ const emit = defineEmits(['active', 'click', 'checkbox-click'])
68
69
 
69
70
  const elRef = ref()
70
71
  const isActive = ref(false)
@@ -92,7 +93,7 @@ onMounted(() => {
92
93
  })
93
94
  })
94
95
 
95
- function onItemClick(e: Event) {
96
+ function onItemClick(e: Event, eventName: 'click' | 'checkbox-click') {
96
97
  if (computedProps.value.disabled) {
97
98
  e.stopPropagation()
98
99
  e.preventDefault()
@@ -101,7 +102,7 @@ function onItemClick(e: Event) {
101
102
  }
102
103
 
103
104
  computedProps.value.onClick && computedProps.value.onClick(e)
104
- emit('click', e)
105
+ emit(eventName, e)
105
106
  }
106
107
 
107
108
  function check() {
@@ -140,7 +141,7 @@ const { styles, classes, className } = useTheme('MenuItem', {}, computedProps, {
140
141
  :class="[
141
142
  className,
142
143
  $style['menu-item'],
143
- [isActive ? $style['menu-item--active'] : ''],
144
+ [isActive && !checkbox ? $style['menu-item--active'] : ''],
144
145
  classes.wrapper,
145
146
  {
146
147
  'flex items-center': $slots.prefix || $slots.suffix
@@ -148,8 +149,16 @@ const { styles, classes, className } = useTheme('MenuItem', {}, computedProps, {
148
149
  ]"
149
150
  :title="computedProps.label"
150
151
  :alt="computedProps.label"
151
- @click="onItemClick"
152
+ @click="onItemClick($event, 'click')"
152
153
  >
154
+ <x-checkbox
155
+ v-if="checkbox"
156
+ :model-value="computedProps.active"
157
+ hide-footer
158
+ skip-form-registry
159
+ @click.stop.prevent="onItemClick($event, 'checkbox-click')"
160
+ />
161
+
153
162
  <span v-if="$slots.prefix || computedProps.prefix" class="mr-2 shrink-0">
154
163
  <slot name="prefix" :item="computedProps">{{ computedProps.prefix }}</slot>
155
164
  </span>
@@ -33,7 +33,7 @@ const theme: MenuItemTheme = {
33
33
  if (props.filled) {
34
34
  if (data.isActive) {
35
35
  return css.variables({
36
- bg: props.selected ? color[100] : color[200],
36
+ bg: props.selected ? color[100] : props.checkbox ? 'transparent' : color[200],
37
37
  text: color[800],
38
38
  hover: {
39
39
  bg: props.selected ? color[200] : color[200],
@@ -7,6 +7,7 @@ const selectProps = {
7
7
  placeholder: String,
8
8
  options: Array as PropType<SelectOption[]>,
9
9
  multiple: Boolean,
10
+ multipleCheckbox: Boolean,
10
11
  truncate: Boolean,
11
12
  flat: Boolean,
12
13
  native: Boolean,
@@ -26,6 +27,7 @@ const selectProps = {
26
27
  type: Number,
27
28
  default: 5,
28
29
  },
30
+ placement: String as PropType<PopoverPlacement>,
29
31
  }
30
32
 
31
33
  export type SelectOption = {
@@ -34,6 +36,8 @@ export type SelectOption = {
34
36
  prefix?: string;
35
37
  suffix?: string;
36
38
  disabled?: boolean;
39
+ keepOpenOnClick?: boolean;
40
+ onClick?: () => void | undefined;
37
41
  }
38
42
 
39
43
  export type SelectProps = ExtractPublicPropTypes<typeof selectProps>
@@ -66,14 +70,16 @@ import XTag from '../tag/Tag.vue'
66
70
  import XIcon from '../icon/Icon.vue'
67
71
  import XMenuItem from '../menu/MenuItem.vue'
68
72
  import XSpinner from '../spinner/Spinner.vue'
69
- import XPopover from '../popover/Popover.vue'
73
+ import XPopover, { type PopoverPlacement } from '../popover/Popover.vue'
70
74
  import XPopoverContainer from '../popover/PopoverContainer.vue'
71
75
  import XInputFooter from '../inputFooter/InputFooter.vue'
72
76
  import XInput from '../input/Input.vue'
73
77
 
74
- const props = defineProps(selectProps)
78
+ const props = defineProps(selectProps)
75
79
 
76
- const emit = defineEmits(useInputtable.emits())
80
+ const emit = defineEmits([...useInputtable.emits(), 'close'])
81
+
82
+ const internalMultiple = computed(() => props.multiple || props.multipleCheckbox)
77
83
 
78
84
  const elRef = ref<HTMLElement | null>(null)
79
85
  const labelRef = ref<InstanceType<typeof XLabel> | null>(null)
@@ -81,14 +87,14 @@ const itemsRef = ref<InstanceType<typeof XMenuItem>[] | null>(null)
81
87
  const popoverRef = ref<InstanceType<typeof XPopover> | null>(null)
82
88
  const selectedIndex = ref<number | undefined>()
83
89
 
84
- const filter = ref('')
90
+ const filter = defineModel('filter', { default : '' })
85
91
  const filterRef = ref<InstanceType<typeof XInput> | null>(null)
86
92
 
87
93
  const isDisabled = computed(() => props.disabled || props.loading || props.readonly)
88
94
 
89
95
  const selected = computed<any | any[]>({
90
96
  get() {
91
- if (props.multiple) {
97
+ if (internalMultiple.value) {
92
98
  if (!props.modelValue) return []
93
99
  if (Array.isArray(props.modelValue)) return props.modelValue
94
100
  else return [props.modelValue]
@@ -109,7 +115,7 @@ const internalOptions = computed(() => {
109
115
  .map((option) => {
110
116
  let isActive = false
111
117
 
112
- if (props.multiple && Array.isArray(selected.value)) {
118
+ if (internalMultiple.value && Array.isArray(selected.value)) {
113
119
  isActive = selected.value.includes(option.value)
114
120
  } else {
115
121
  isActive = option.value === selected.value
@@ -122,7 +128,8 @@ const internalOptions = computed(() => {
122
128
  prefix: option.prefix,
123
129
  suffix: option.suffix,
124
130
  disabled: option.disabled,
125
- iconRight: isActive ? checkIcon : undefined,
131
+ iconRight: !props.multipleCheckbox && isActive ? checkIcon : undefined,
132
+ keepOpenOnClick: option.keepOpenOnClick,
126
133
  onClick: () => handleOptionClick(option.value),
127
134
  }
128
135
  })
@@ -154,7 +161,7 @@ watch(isOpen, (isOpenValue) => {
154
161
  if (isOpenValue) {
155
162
  findSelectedIndex()
156
163
 
157
- if (props.multiple || typeof selectedIndex.value === 'undefined') {
164
+ if (internalMultiple.value || typeof selectedIndex.value === 'undefined') {
158
165
  findSelectableIndex(-1)
159
166
  }
160
167
 
@@ -168,11 +175,12 @@ watch(isOpen, (isOpenValue) => {
168
175
 
169
176
  } else {
170
177
  if (props.filterable) filter.value = ''
178
+ emit('close')
171
179
  }
172
180
  })
173
181
 
174
182
  function findSelectedIndex() {
175
- if (props.multiple) {
183
+ if (internalMultiple.value) {
176
184
  if (Array.isArray(selected.value) && selected.value.length > 0) {
177
185
  const index = internalOptions.value.findIndex((option) => option.value === selected.value[0])
178
186
 
@@ -229,32 +237,37 @@ function handleOptionClick(value: string | number) {
229
237
 
230
238
  if (!option || option.disabled) return
231
239
 
232
- if (props.multiple) {
233
- if (Array.isArray(selected.value)) {
234
- const index = selected.value.indexOf(value)
235
-
236
- if (index !== -1) selected.value.splice(index, 1)
237
- else {
238
- selected.value.push(value)
239
- emit('update:modelValue', selected.value)
240
+ if (option.onClick) {
241
+ option.onClick()
242
+ } else {
243
+ if (internalMultiple.value) {
244
+ if (Array.isArray(selected.value)) {
245
+ const index = selected.value.indexOf(value)
246
+
247
+ if (index !== -1) selected.value.splice(index, 1)
248
+ else {
249
+ selected.value.push(value)
250
+ }
251
+ } else {
252
+ selected.value = [value]
240
253
  }
254
+
255
+ emit('update:modelValue', selected.value)
256
+
257
+ if (props.filterable)
258
+ setTimeout(() => {
259
+ filterRef.value?.focus()
260
+ })
241
261
  } else {
242
- selected.value = [value]
262
+ selected.value = value
243
263
  }
244
264
 
245
- if (props.filterable)
246
- setTimeout(() => {
247
- filterRef.value?.focus()
265
+ if (!props.native) {
266
+ nextTick(() => {
267
+ validate()
268
+ labelRef.value?.$el.focus()
248
269
  })
249
- } else {
250
- selected.value = value
251
- }
252
-
253
- if (!props.native) {
254
- nextTick(() => {
255
- validate()
256
- labelRef.value?.$el.focus()
257
- })
270
+ }
258
271
  }
259
272
  }
260
273
 
@@ -360,8 +373,11 @@ function handleKeyNavigation(e: KeyboardEvent) {
360
373
  return
361
374
  }
362
375
  if (typeof selectedIndex.value !== 'undefined' && internalOptions.value[selectedIndex.value]) {
363
- handleOptionClick(internalOptions.value[selectedIndex.value].value)
364
- if (!props.multiple) popoverRef.value?.hide()
376
+ const selectedItemInternalOptions = internalOptions.value[selectedIndex.value]
377
+
378
+ handleOptionClick(selectedItemInternalOptions.value)
379
+
380
+ if (!selectedItemInternalOptions.keepOpenOnClick && (!internalMultiple.value || props.multipleCheckbox)) popoverRef.value?.hide()
365
381
  }
366
382
  } else if (e.code === 'Tab') {
367
383
  if (isOpen.value) {
@@ -383,7 +399,7 @@ const showCountTag = ref(false)
383
399
  const hiddenTags = ref(0)
384
400
 
385
401
  const handleTruncate = useThrottleFn(() => {
386
- if (props.multiple && props.truncate) {
402
+ if (internalMultiple.value && props.truncate) {
387
403
  nextTick(() => {
388
404
  const maxTags = calcMaxTags()
389
405
 
@@ -432,7 +448,7 @@ watch(selected, (val) => {
432
448
 
433
449
  const { styles, classes, className } = useTheme('Select', {}, props, { errorInternal })
434
450
 
435
- defineExpose({ focus, blur, reset, validate, setError })
451
+ defineExpose({ focus, blur, reset, validate, setError, filterRef })
436
452
  </script>
437
453
 
438
454
  <template>
@@ -453,7 +469,7 @@ defineExpose({ focus, blur, reset, validate, setError })
453
469
  v-on="labelListeners"
454
470
  >
455
471
  <div class="relative">
456
- <div v-if="native && !multiple" :class="classes.box" @click="elRef?.click()">
472
+ <div v-if="native && !internalMultiple" :class="classes.box" @click="elRef?.click()">
457
473
  <template v-if="!isEmpty(selected)">
458
474
  {{ getLabel(selected) }}
459
475
  </template>
@@ -468,6 +484,7 @@ defineExpose({ focus, blur, reset, validate, setError })
468
484
  <x-popover
469
485
  ref="popoverRef"
470
486
  :disabled="isDisabled"
487
+ :placement="placement"
471
488
  >
472
489
  <slot
473
490
  name="input"
@@ -477,7 +494,7 @@ defineExpose({ focus, blur, reset, validate, setError })
477
494
  :label="getLabel(selected)"
478
495
  >
479
496
  <div :class="[classes.box]">
480
- <template v-if="multiple && Array.isArray(selected) && selected.length > 0">
497
+ <template v-if="internalMultiple && Array.isArray(selected) && selected.length > 0">
481
498
  <div
482
499
  ref="tagsRef"
483
500
  class="flex gap-1 relative"
@@ -494,7 +511,12 @@ defineExpose({ focus, blur, reset, validate, setError })
494
511
  :outlined="!(isDisabled || options?.find((i) => i.value === value)?.disabled)"
495
512
  :disabled="isDisabled || options?.find((i) => i.value === value)?.disabled"
496
513
  @remove="(e: Event) => { handleRemove(e, value) }"
497
- >{{ getLabel(value) }}</x-tag>
514
+ >
515
+ <template #prefix>
516
+ <slot name="tag-prefix" :item="options?.find((i) => i.value === value)"></slot>
517
+ </template>
518
+ {{ getLabel(value) }}
519
+ </x-tag>
498
520
 
499
521
  <div
500
522
  v-if="showCountTag"
@@ -503,7 +525,7 @@ defineExpose({ focus, blur, reset, validate, setError })
503
525
  >+{{ hiddenTags }}</div>
504
526
  </div>
505
527
  </template>
506
- <template v-else-if="!multiple && !isEmpty(selected)">
528
+ <template v-else-if="!internalMultiple && !isEmpty(selected)">
507
529
  {{ getLabel(selected) }}
508
530
  </template>
509
531
 
@@ -530,6 +552,7 @@ defineExpose({ focus, blur, reset, validate, setError })
530
552
  v-model="filter"
531
553
  :placeholder="filterPlaceholder"
532
554
  skip-form-registry
555
+ data-1p-ignore
533
556
  size="sm"
534
557
  />
535
558
  </div>
@@ -544,9 +567,10 @@ defineExpose({ focus, blur, reset, validate, setError })
544
567
  :size="size"
545
568
  :disabled="item.data.disabled"
546
569
  :selected="item.index === selectedIndex"
570
+ :checkbox="multipleCheckbox && !item.data.keepOpenOnClick"
547
571
  :color="color"
548
572
  filled
549
- @click="() => !multiple && popoverRef?.hide()"
573
+ @click="() => !item.data.keepOpenOnClick && (!internalMultiple || multipleCheckbox) && popoverRef?.hide()"
550
574
  >
551
575
  <template v-if="$slots.prefix || item.data.prefix" #prefix><slot name="prefix" :item="item.data">{{ item.data.prefix }}</slot></template>
552
576
  <slot name="label" :item="item.data"></slot>
@@ -562,7 +586,7 @@ defineExpose({ focus, blur, reset, validate, setError })
562
586
  </template>
563
587
  </x-popover>
564
588
  <x-popover
565
- v-if="multiple && truncate && showCountTag"
589
+ v-if="internalMultiple && truncate && showCountTag"
566
590
  ref="multipleHiddenRef"
567
591
  :popper-show-triggers="[]"
568
592
  :popper-hide-triggers="[]"
@@ -579,7 +603,12 @@ defineExpose({ focus, blur, reset, validate, setError })
579
603
  :outlined="!(isDisabled || options?.find((i) => i.value === value)?.disabled)"
580
604
  :disabled="isDisabled || options?.find((i) => i.value === value)?.disabled"
581
605
  @remove="(e: Event) => { handleRemove(e, value) }"
582
- >{{ getLabel(value) }}</x-tag>
606
+ >
607
+ <template #prefix>
608
+ <slot name="tag-prefix" :item="options?.find((i) => i.value === value)"></slot>
609
+ </template>
610
+ {{ getLabel(value) }}
611
+ </x-tag>
583
612
  </x-popover-container>
584
613
  </template>
585
614
  </x-popover>
@@ -590,10 +619,10 @@ defineExpose({ focus, blur, reset, validate, setError })
590
619
  ref="elRef"
591
620
  v-model="selected"
592
621
  tabindex="-1"
593
- :class="native && !multiple ? 'absolute inset-0 w-full h-full cursor-pointer opacity-0' : 'hidden'"
622
+ :class="native && !internalMultiple ? 'absolute inset-0 w-full h-full cursor-pointer opacity-0' : 'hidden'"
594
623
  :name="name"
595
624
  :disabled="disabled || loading"
596
- :multiple="multiple"
625
+ :multiple="internalMultiple"
597
626
  :readonly="readonly"
598
627
  v-on="inputListeners"
599
628
  >
@@ -609,7 +638,7 @@ defineExpose({ focus, blur, reset, validate, setError })
609
638
  </template>
610
639
  </select>
611
640
 
612
- <div :class="classes.iconWrapper">
641
+ <div v-if="!$slots.input" :class="classes.iconWrapper">
613
642
  <x-spinner v-if="loading" :size="size" />
614
643
  <slot v-else name="icon">
615
644
  <x-icon
@@ -617,7 +646,6 @@ defineExpose({ focus, blur, reset, validate, setError })
617
646
  :class="[classes.icon]"
618
647
  />
619
648
  </slot>
620
-
621
649
  </div>
622
650
  </div>
623
651
 
@@ -67,11 +67,18 @@ const { styles, classes, className } = useTheme('Tag', {}, props)
67
67
  ]"
68
68
  >
69
69
  <span
70
- v-if="removable"
71
- class="max-w-full truncate pr-4"
70
+ class="max-w-full"
71
+ :class="{'pr-4': removable }"
72
72
  >
73
- <slot></slot>
74
- <div class="absolute right-1.5 top-0 h-full flex items-center">
73
+ <div class="flex items-center gap-2">
74
+ <slot name="prefix"></slot>
75
+
76
+ <div class="truncate">
77
+ <slot></slot>
78
+ </div>
79
+ </div>
80
+
81
+ <div v-if="removable" class="absolute right-1.5 top-0 h-full flex items-center">
75
82
  <x-icon
76
83
  :size="closeIconSize"
77
84
  :icon="closeIcon"
@@ -81,7 +88,5 @@ const { styles, classes, className } = useTheme('Tag', {}, props)
81
88
  />
82
89
  </div>
83
90
  </span>
84
-
85
- <slot v-else></slot>
86
91
  </component>
87
92
  </template>
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '1.10.5'
1
+ export default '1.12.0'