@indielayer/ui 1.10.5 → 1.11.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.11.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.11.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.11.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,
@@ -15,6 +16,7 @@ const selectProps = {
15
16
  type: String,
16
17
  default: 'Filter by...',
17
18
  },
19
+ filterInputProps: Object,
18
20
  virtualList: Boolean,
19
21
  virtualListOffsetTop: Number,
20
22
  virtualListOffsetBottom: Number,
@@ -26,6 +28,7 @@ const selectProps = {
26
28
  type: Number,
27
29
  default: 5,
28
30
  },
31
+ placement: String as PropType<PopoverPlacement>,
29
32
  }
30
33
 
31
34
  export type SelectOption = {
@@ -34,6 +37,8 @@ export type SelectOption = {
34
37
  prefix?: string;
35
38
  suffix?: string;
36
39
  disabled?: boolean;
40
+ keepOpenOnClick?: boolean;
41
+ onClick?: () => void | undefined;
37
42
  }
38
43
 
39
44
  export type SelectProps = ExtractPublicPropTypes<typeof selectProps>
@@ -66,14 +71,16 @@ import XTag from '../tag/Tag.vue'
66
71
  import XIcon from '../icon/Icon.vue'
67
72
  import XMenuItem from '../menu/MenuItem.vue'
68
73
  import XSpinner from '../spinner/Spinner.vue'
69
- import XPopover from '../popover/Popover.vue'
74
+ import XPopover, { type PopoverPlacement } from '../popover/Popover.vue'
70
75
  import XPopoverContainer from '../popover/PopoverContainer.vue'
71
76
  import XInputFooter from '../inputFooter/InputFooter.vue'
72
77
  import XInput from '../input/Input.vue'
73
78
 
74
- const props = defineProps(selectProps)
79
+ const props = defineProps(selectProps)
75
80
 
76
- const emit = defineEmits(useInputtable.emits())
81
+ const emit = defineEmits([...useInputtable.emits(), 'close'])
82
+
83
+ const internalMultiple = computed(() => props.multiple || props.multipleCheckbox)
77
84
 
78
85
  const elRef = ref<HTMLElement | null>(null)
79
86
  const labelRef = ref<InstanceType<typeof XLabel> | null>(null)
@@ -81,14 +88,14 @@ const itemsRef = ref<InstanceType<typeof XMenuItem>[] | null>(null)
81
88
  const popoverRef = ref<InstanceType<typeof XPopover> | null>(null)
82
89
  const selectedIndex = ref<number | undefined>()
83
90
 
84
- const filter = ref('')
91
+ const filter = defineModel('filter', { default : '' })
85
92
  const filterRef = ref<InstanceType<typeof XInput> | null>(null)
86
93
 
87
94
  const isDisabled = computed(() => props.disabled || props.loading || props.readonly)
88
95
 
89
96
  const selected = computed<any | any[]>({
90
97
  get() {
91
- if (props.multiple) {
98
+ if (internalMultiple.value) {
92
99
  if (!props.modelValue) return []
93
100
  if (Array.isArray(props.modelValue)) return props.modelValue
94
101
  else return [props.modelValue]
@@ -109,7 +116,7 @@ const internalOptions = computed(() => {
109
116
  .map((option) => {
110
117
  let isActive = false
111
118
 
112
- if (props.multiple && Array.isArray(selected.value)) {
119
+ if (internalMultiple.value && Array.isArray(selected.value)) {
113
120
  isActive = selected.value.includes(option.value)
114
121
  } else {
115
122
  isActive = option.value === selected.value
@@ -122,7 +129,8 @@ const internalOptions = computed(() => {
122
129
  prefix: option.prefix,
123
130
  suffix: option.suffix,
124
131
  disabled: option.disabled,
125
- iconRight: isActive ? checkIcon : undefined,
132
+ iconRight: !props.multipleCheckbox && isActive ? checkIcon : undefined,
133
+ keepOpenOnClick: option.keepOpenOnClick,
126
134
  onClick: () => handleOptionClick(option.value),
127
135
  }
128
136
  })
@@ -154,7 +162,7 @@ watch(isOpen, (isOpenValue) => {
154
162
  if (isOpenValue) {
155
163
  findSelectedIndex()
156
164
 
157
- if (props.multiple || typeof selectedIndex.value === 'undefined') {
165
+ if (internalMultiple.value || typeof selectedIndex.value === 'undefined') {
158
166
  findSelectableIndex(-1)
159
167
  }
160
168
 
@@ -168,11 +176,12 @@ watch(isOpen, (isOpenValue) => {
168
176
 
169
177
  } else {
170
178
  if (props.filterable) filter.value = ''
179
+ emit('close')
171
180
  }
172
181
  })
173
182
 
174
183
  function findSelectedIndex() {
175
- if (props.multiple) {
184
+ if (internalMultiple.value) {
176
185
  if (Array.isArray(selected.value) && selected.value.length > 0) {
177
186
  const index = internalOptions.value.findIndex((option) => option.value === selected.value[0])
178
187
 
@@ -229,32 +238,36 @@ function handleOptionClick(value: string | number) {
229
238
 
230
239
  if (!option || option.disabled) return
231
240
 
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)
241
+ if (option.onClick) {
242
+ option.onClick()
243
+ } else {
244
+ if (internalMultiple.value) {
245
+ if (Array.isArray(selected.value)) {
246
+ const index = selected.value.indexOf(value)
247
+
248
+ if (index !== -1) selected.value.splice(index, 1)
249
+ else {
250
+ selected.value.push(value)
251
+ emit('update:modelValue', selected.value)
252
+ }
253
+ } else {
254
+ selected.value = [value]
240
255
  }
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"
@@ -503,7 +520,7 @@ defineExpose({ focus, blur, reset, validate, setError })
503
520
  >+{{ hiddenTags }}</div>
504
521
  </div>
505
522
  </template>
506
- <template v-else-if="!multiple && !isEmpty(selected)">
523
+ <template v-else-if="!internalMultiple && !isEmpty(selected)">
507
524
  {{ getLabel(selected) }}
508
525
  </template>
509
526
 
@@ -530,7 +547,9 @@ defineExpose({ focus, blur, reset, validate, setError })
530
547
  v-model="filter"
531
548
  :placeholder="filterPlaceholder"
532
549
  skip-form-registry
550
+ data-1p-ignore
533
551
  size="sm"
552
+ v-bind="filterInputProps"
534
553
  />
535
554
  </div>
536
555
  </slot>
@@ -544,9 +563,10 @@ defineExpose({ focus, blur, reset, validate, setError })
544
563
  :size="size"
545
564
  :disabled="item.data.disabled"
546
565
  :selected="item.index === selectedIndex"
566
+ :checkbox="multipleCheckbox && !item.data.keepOpenOnClick"
547
567
  :color="color"
548
568
  filled
549
- @click="() => !multiple && popoverRef?.hide()"
569
+ @click="() => !item.data.keepOpenOnClick && (!internalMultiple || multipleCheckbox) && popoverRef?.hide()"
550
570
  >
551
571
  <template v-if="$slots.prefix || item.data.prefix" #prefix><slot name="prefix" :item="item.data">{{ item.data.prefix }}</slot></template>
552
572
  <slot name="label" :item="item.data"></slot>
@@ -562,7 +582,7 @@ defineExpose({ focus, blur, reset, validate, setError })
562
582
  </template>
563
583
  </x-popover>
564
584
  <x-popover
565
- v-if="multiple && truncate && showCountTag"
585
+ v-if="internalMultiple && truncate && showCountTag"
566
586
  ref="multipleHiddenRef"
567
587
  :popper-show-triggers="[]"
568
588
  :popper-hide-triggers="[]"
@@ -590,10 +610,10 @@ defineExpose({ focus, blur, reset, validate, setError })
590
610
  ref="elRef"
591
611
  v-model="selected"
592
612
  tabindex="-1"
593
- :class="native && !multiple ? 'absolute inset-0 w-full h-full cursor-pointer opacity-0' : 'hidden'"
613
+ :class="native && !internalMultiple ? 'absolute inset-0 w-full h-full cursor-pointer opacity-0' : 'hidden'"
594
614
  :name="name"
595
615
  :disabled="disabled || loading"
596
- :multiple="multiple"
616
+ :multiple="internalMultiple"
597
617
  :readonly="readonly"
598
618
  v-on="inputListeners"
599
619
  >
@@ -609,7 +629,7 @@ defineExpose({ focus, blur, reset, validate, setError })
609
629
  </template>
610
630
  </select>
611
631
 
612
- <div :class="classes.iconWrapper">
632
+ <div v-if="!$slots.input" :class="classes.iconWrapper">
613
633
  <x-spinner v-if="loading" :size="size" />
614
634
  <slot v-else name="icon">
615
635
  <x-icon
@@ -617,7 +637,6 @@ defineExpose({ focus, blur, reset, validate, setError })
617
637
  :class="[classes.icon]"
618
638
  />
619
639
  </slot>
620
-
621
640
  </div>
622
641
  </div>
623
642
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '1.10.5'
1
+ export default '1.11.0'