sprintify-ui 0.10.25 → 0.10.27

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.
@@ -9,6 +9,8 @@ import BaseInput from './BaseInput.vue';
9
9
  import BaseSelect from './BaseSelect.vue';
10
10
  declare const emit: (event: "close" | "select" | "open" | "clear" | "update:modelValue" | "scrollBottom" | "typing", ...args: any[]) => void;
11
11
  declare const hasErrorInternal: ComputedRef<boolean>, sizeInternal: ComputedRef<"xs" | "sm" | "md" | "lg" | "xl">;
12
+ declare const inputRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
13
+ declare const dropdownRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
12
14
  declare const drawer: import("vue").Ref<({
13
15
  $: import("vue").ComponentInternalInstance;
14
16
  $data: {};
@@ -349,18 +351,32 @@ declare const slotProps: {
349
351
  close: typeof close;
350
352
  keywords: ComputedRef<string>;
351
353
  };
354
+ declare const inputRefWidth: ComputedRef<number>;
355
+ declare const floatingStyles: Readonly<import("vue").Ref<{
356
+ position: import("@floating-ui/utils").Strategy;
357
+ top: string;
358
+ left: string;
359
+ transform?: string;
360
+ willChange?: string;
361
+ }, {
362
+ position: import("@floating-ui/utils").Strategy;
363
+ top: string;
364
+ left: string;
365
+ transform?: string;
366
+ willChange?: string;
367
+ }>>;
352
368
  declare const deleteButtonIconClasses: ComputedRef<string[]>;
353
369
  declare const inputClasses: ComputedRef<string | string[]>;
354
370
  declare const selectClasses: ComputedRef<string | string[]>;
355
371
  declare const showRemoveButtonInternal: ComputedRef<boolean | null>;
356
372
  declare const __VLS_ctx: InstanceType<__VLS_PickNotAny<typeof __VLS_self, new () => {}>>;
357
- declare var __VLS_33: {
373
+ declare var __VLS_37: {
358
374
  focus: typeof focus;
359
375
  blur: typeof blur;
360
376
  open: typeof open;
361
377
  close: typeof close;
362
378
  keywords: ComputedRef<string>;
363
- }, __VLS_35: {
379
+ }, __VLS_39: {
364
380
  focus: typeof focus;
365
381
  blur: typeof blur;
366
382
  open: typeof open;
@@ -371,7 +387,7 @@ declare var __VLS_33: {
371
387
  label: string;
372
388
  selected: boolean;
373
389
  active: boolean;
374
- }, __VLS_37: {
390
+ }, __VLS_41: {
375
391
  focus: typeof focus;
376
392
  blur: typeof blur;
377
393
  open: typeof open;
@@ -380,11 +396,11 @@ declare var __VLS_33: {
380
396
  options: NormalizedOption[];
381
397
  };
382
398
  type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
383
- empty?: (props: typeof __VLS_33) => any;
399
+ empty?: (props: typeof __VLS_37) => any;
384
400
  } & {
385
- option?: (props: typeof __VLS_35) => any;
401
+ option?: (props: typeof __VLS_39) => any;
386
402
  } & {
387
- footer?: (props: typeof __VLS_37) => any;
403
+ footer?: (props: typeof __VLS_41) => any;
388
404
  }>;
389
405
  declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
390
406
  modelValue: {
@@ -494,6 +510,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
494
510
  emit: typeof emit;
495
511
  hasErrorInternal: typeof hasErrorInternal;
496
512
  sizeInternal: typeof sizeInternal;
513
+ inputRef: typeof inputRef;
514
+ dropdownRef: typeof dropdownRef;
497
515
  drawer: typeof drawer;
498
516
  keywords: typeof keywords;
499
517
  autocomplete: typeof autocomplete;
@@ -510,6 +528,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
510
528
  selection: typeof selection;
511
529
  onSelectChange: typeof onSelectChange;
512
530
  slotProps: typeof slotProps;
531
+ inputRefWidth: typeof inputRefWidth;
532
+ floatingStyles: typeof floatingStyles;
513
533
  deleteButtonIconClasses: typeof deleteButtonIconClasses;
514
534
  inputClasses: typeof inputClasses;
515
535
  selectClasses: typeof selectClasses;
@@ -4,6 +4,8 @@ import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
4
4
  import { t } from '@/i18n';
5
5
  import { Size } from '@/utils/sizes';
6
6
  declare const emit: (event: "close" | "open" | "update:modelValue" | "scrollBottom" | "typing", ...args: any[]) => void;
7
+ declare const inputRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
8
+ declare const dropdownRef: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
7
9
  declare const drawer: import("vue").Ref<({
8
10
  $: import("vue").ComponentInternalInstance;
9
11
  $data: {};
@@ -341,6 +343,20 @@ declare const slotProps: {
341
343
  keywords: ComputedRef<string>;
342
344
  };
343
345
  declare const removeOption: (option: NormalizedOption) => void;
346
+ declare const inputRefWidth: ComputedRef<number>;
347
+ declare const floatingStyles: Readonly<import("vue").Ref<{
348
+ position: import("@floating-ui/utils").Strategy;
349
+ top: string;
350
+ left: string;
351
+ transform?: string;
352
+ willChange?: string;
353
+ }, {
354
+ position: import("@floating-ui/utils").Strategy;
355
+ top: string;
356
+ left: string;
357
+ transform?: string;
358
+ willChange?: string;
359
+ }>>;
344
360
  declare const wrapperClass: ComputedRef<string | string[]>;
345
361
  declare const inputClasses: ComputedRef<string[]>;
346
362
  declare const selectionClass: (selection: NormalizedOption) => string;
@@ -350,13 +366,13 @@ declare var __VLS_1: {
350
366
  items: NormalizedOption[];
351
367
  removeOption: (option: NormalizedOption) => void;
352
368
  disabled: boolean;
353
- }, __VLS_13: {
369
+ }, __VLS_17: {
354
370
  focus: typeof focus;
355
371
  blur: typeof blur;
356
372
  open: typeof open;
357
373
  close: typeof close;
358
374
  keywords: ComputedRef<string>;
359
- }, __VLS_15: {
375
+ }, __VLS_19: {
360
376
  focus: typeof focus;
361
377
  blur: typeof blur;
362
378
  open: typeof open;
@@ -367,7 +383,7 @@ declare var __VLS_1: {
367
383
  label: string;
368
384
  selected: boolean;
369
385
  active: boolean;
370
- }, __VLS_17: {
386
+ }, __VLS_21: {
371
387
  focus: typeof focus;
372
388
  blur: typeof blur;
373
389
  open: typeof open;
@@ -378,11 +394,11 @@ declare var __VLS_1: {
378
394
  type __VLS_Slots = __VLS_PrettifyGlobal<__VLS_OmitStringIndex<typeof __VLS_ctx.$slots> & {
379
395
  items?: (props: typeof __VLS_1) => any;
380
396
  } & {
381
- empty?: (props: typeof __VLS_13) => any;
397
+ empty?: (props: typeof __VLS_17) => any;
382
398
  } & {
383
- option?: (props: typeof __VLS_15) => any;
399
+ option?: (props: typeof __VLS_19) => any;
384
400
  } & {
385
- footer?: (props: typeof __VLS_17) => any;
401
+ footer?: (props: typeof __VLS_21) => any;
386
402
  }>;
387
403
  declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
388
404
  modelValue: {
@@ -465,6 +481,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
465
481
  BaseAutocompleteDrawer: typeof BaseAutocompleteDrawer;
466
482
  t: typeof t;
467
483
  emit: typeof emit;
484
+ inputRef: typeof inputRef;
485
+ dropdownRef: typeof dropdownRef;
468
486
  drawer: typeof drawer;
469
487
  keywords: typeof keywords;
470
488
  autocomplete: typeof autocomplete;
@@ -478,6 +496,8 @@ declare const __VLS_self: import("vue").DefineComponent<import("vue").ExtractPro
478
496
  onSelect: typeof onSelect;
479
497
  slotProps: typeof slotProps;
480
498
  removeOption: typeof removeOption;
499
+ inputRefWidth: typeof inputRefWidth;
500
+ floatingStyles: typeof floatingStyles;
481
501
  wrapperClass: typeof wrapperClass;
482
502
  inputClasses: typeof inputClasses;
483
503
  selectionClass: typeof selectionClass;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.10.25",
3
+ "version": "0.10.27",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "rimraf dist && vue-tsc && vite build",
@@ -1,5 +1,7 @@
1
1
  import { createFieldStory, options, sizes } from "../../.storybook/utils";
2
2
  import BaseAutocomplete from "./BaseAutocomplete.vue";
3
+ import BaseCard from "./BaseCard.vue";
4
+ import BaseCardRow from "./BaseCardRow.vue";
3
5
  import ShowValue from "@/../.storybook/components/ShowValue.vue";
4
6
  import { computed } from "vue";
5
7
 
@@ -25,13 +27,17 @@ export default {
25
27
  };
26
28
 
27
29
  const Template = (args) => ({
28
- components: { BaseAutocomplete, ShowValue },
30
+ components: { BaseAutocomplete, BaseCard, BaseCardRow, ShowValue },
29
31
  setup() {
30
32
  const value = ref(options[2]);
31
33
  return { args, value };
32
34
  },
33
35
  template: `
34
- <BaseAutocomplete v-model="value" v-bind="args"></BaseAutocomplete>
36
+ <BaseCard clipped>
37
+ <BaseCardRow>
38
+ <BaseAutocomplete v-model="value" v-bind="args"></BaseAutocomplete>
39
+ </BaseCardRow>
40
+ </BaseCard>
35
41
  <ShowValue :value="value" />
36
42
  `,
37
43
  });
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <div ref="autocomplete">
3
- <div class="relative z-[1]">
3
+ <div
4
+ ref="inputRef"
5
+ class="relative z-[1]"
6
+ >
4
7
  <div
5
8
  class="flex"
6
9
  :class="[
@@ -61,15 +64,19 @@
61
64
  </button>
62
65
  </div>
63
66
  </div>
64
-
65
- <div class="relative">
67
+ <Teleport
68
+ to="body"
69
+ :disabled="inline"
70
+ >
66
71
  <div
67
72
  v-show="dropdownOpened"
68
- class="w-full min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
73
+ ref="dropdownRef"
74
+ :style="!inline ? { ...floatingStyles, width: inputRefWidth + 'px' } : {}"
75
+ class="min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
69
76
  :class="[
70
77
  inline
71
78
  ? 'relative border-b border-x rounded-t-none'
72
- : 'absolute top-1 border shadow-2xl z-menu',
79
+ : 'fixed border shadow-2xl z-menu',
73
80
  ]"
74
81
  >
75
82
  <BaseAutocompleteDrawer
@@ -104,7 +111,7 @@
104
111
  </template>
105
112
  </BaseAutocompleteDrawer>
106
113
  </div>
107
- </div>
114
+ </Teleport>
108
115
  </div>
109
116
  </template>
110
117
 
@@ -122,6 +129,8 @@ import { Size, sizes } from '@/utils/sizes';
122
129
  import BaseInput from './BaseInput.vue';
123
130
  import BaseSelect from './BaseSelect.vue';
124
131
  import { twMerge } from 'tailwind-merge';
132
+ import { autoUpdate, flip, offset, useFloating } from '@floating-ui/vue';
133
+ import { useElementBounding } from '@vueuse/core';
125
134
 
126
135
  const props = defineProps({
127
136
  modelValue: {
@@ -266,6 +275,8 @@ onBeforeUnmount(() => {
266
275
  clearTimeout(openOfFocusTimeout);
267
276
  });
268
277
 
278
+ const inputRef = ref<HTMLElement | null>(null);
279
+ const dropdownRef = ref<HTMLElement | null>(null);
269
280
  const drawer = ref<InstanceType<typeof BaseAutocompleteDrawer> | null>(null);
270
281
 
271
282
  let timerId = 0;
@@ -279,8 +290,7 @@ const opened = ref(false);
279
290
  const dropdownOpened = computed(() => opened.value || props.dropdownShow == 'always');
280
291
 
281
292
  const normalizedOptions = hasOptions.normalizedOptions;
282
- const normalizedModelValue =
283
- hasOptions.normalizedModelValue as ComputedRef<NormalizedOption | null>;
293
+ const normalizedModelValue = hasOptions.normalizedModelValue as ComputedRef<NormalizedOption | null>;
284
294
 
285
295
  const filteredNormalizedOptions = computed((): NormalizedOption[] => {
286
296
  let options = normalizedOptions.value;
@@ -330,7 +340,7 @@ watch(
330
340
 
331
341
  useClickOutside(autocomplete, () => {
332
342
  close();
333
- });
343
+ }, { includes: () => [dropdownRef] });
334
344
 
335
345
  onMounted(() => {
336
346
  window.addEventListener('keydown', onWindowKeydown);
@@ -510,6 +520,20 @@ const slotProps = {
510
520
  keywords: computed(() => keywords.value),
511
521
  };
512
522
 
523
+ // Floating UI
524
+
525
+ const inputRefRect = useElementBounding(inputRef);
526
+ const inputRefWidth = computed(() => {
527
+ return inputRefRect.width.value;
528
+ });
529
+
530
+ const { floatingStyles } = useFloating(inputRef, dropdownRef, {
531
+ middleware: [offset(4), flip({
532
+ fallbackPlacements: ['right', 'bottom'],
533
+ })],
534
+ whileElementsMounted: autoUpdate,
535
+ });
536
+
513
537
  // Element Classes
514
538
 
515
539
  const deleteButtonIconClasses = computed(() => {
@@ -135,9 +135,10 @@ const emit = defineEmits(['update:modelValue']);
135
135
  const autocomplete = ref<InstanceType<typeof BaseAutocomplete> | null>(null);
136
136
 
137
137
  const model = computed(() => {
138
- return props.modelValue
139
- ? props.options.find((option) => (option[props.primaryKey] + '') === (props.modelValue + ''))
140
- : null;
138
+ if (!props.modelValue) {
139
+ return null;
140
+ }
141
+ return props.options.find((option) => (option[props.primaryKey] + '') === (props.modelValue + ''));
141
142
  });
142
143
 
143
144
  function onUpdate(newModel: RawOption | null) {
@@ -198,7 +198,7 @@ const DEFAULT_QUERY = {
198
198
  </script>
199
199
 
200
200
  <script lang="ts" setup>
201
- import { cloneDeep, debounce, merge, set, sortBy } from 'lodash';
201
+ import { cloneDeep, debounce, merge, set, sortBy, uniqueId } from 'lodash';
202
202
  import hash from 'object-hash';
203
203
  import { PropType } from 'vue';
204
204
  import {
@@ -661,6 +661,8 @@ function fetchWithoutLoading(force = false) {
661
661
  fetch(force, false);
662
662
  }
663
663
 
664
+ let requestId = '';
665
+
664
666
  function fetch(force = false, showLoading = true) {
665
667
  if (willUnmount) {
666
668
  return;
@@ -682,6 +684,9 @@ function fetch(force = false, showLoading = true) {
682
684
  return;
683
685
  }
684
686
 
687
+ const requestIdInternal = uniqueId();
688
+ requestId = requestIdInternal;
689
+
685
690
  if (showLoading) {
686
691
  loading.value = true;
687
692
  }
@@ -691,6 +696,10 @@ function fetch(force = false, showLoading = true) {
691
696
  http
692
697
  .get(fullUrl)
693
698
  .then((response) => {
699
+ if (requestIdInternal !== requestId) {
700
+ return;
701
+ }
702
+
694
703
  data.value = response.data;
695
704
  error.value = false;
696
705
  firstLoad.value = true;
@@ -2,6 +2,8 @@ import { createFieldStory, options, sizes } from "../../.storybook/utils";
2
2
  import BaseTagAutocomplete from "./BaseTagAutocomplete.vue";
3
3
  import ShowValue from "@/../.storybook/components/ShowValue.vue";
4
4
  import BaseAppSnackbars from "./BaseAppSnackbars.vue";
5
+ import BaseCard from "./BaseCard.vue";
6
+ import BaseCardRow from "./BaseCardRow.vue";
5
7
 
6
8
  export default {
7
9
  title: "Form/BaseTagAutocomplete",
@@ -25,15 +27,19 @@ export default {
25
27
  };
26
28
 
27
29
  const Template = (args) => ({
28
- components: { BaseTagAutocomplete, ShowValue, BaseAppSnackbars },
30
+ components: { BaseTagAutocomplete, BaseCard, BaseCardRow, ShowValue, BaseAppSnackbars },
29
31
  setup() {
30
32
  const value = ref(null);
31
33
  return { args, value };
32
34
  },
33
35
  template: `
34
- <BaseTagAutocomplete v-model="value" v-bind="args"></BaseTagAutocomplete>
35
- <ShowValue :value="value" />
36
- <BaseAppSnackbars />
36
+ <BaseCard clipped>
37
+ <BaseCardRow>
38
+ <BaseTagAutocomplete v-model="value" v-bind="args"></BaseTagAutocomplete>
39
+ </BaseCardRow>
40
+ </BaseCard>
41
+ <ShowValue :value="value" />
42
+ <BaseAppSnackbars />
37
43
  `,
38
44
  });
39
45
 
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <div ref="autocomplete">
3
- <div :class="wrapperClass">
3
+ <div
4
+ ref="inputRef"
5
+ :class="wrapperClass"
6
+ >
4
7
  <slot
5
8
  name="items"
6
9
  :items="normalizedModelValue"
@@ -44,14 +47,19 @@
44
47
  >
45
48
  </div>
46
49
 
47
- <div class="relative">
50
+ <Teleport
51
+ to="body"
52
+ :disabled="inline"
53
+ >
48
54
  <div
49
- v-if="dropdownOpened"
50
- class="w-full min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
55
+ v-show="dropdownOpened"
56
+ ref="dropdownRef"
57
+ :style="!inline ? { ...floatingStyles, width: inputRefWidth + 'px' } : {}"
58
+ class="min-h-[110px] overflow-hidden bg-white border-slate-300 input-rounded"
51
59
  :class="[
52
60
  inline
53
61
  ? 'relative border-b border-x rounded-t-none'
54
- : 'absolute top-1 border shadow-2xl z-menu',
62
+ : 'fixed border shadow-2xl z-menu',
55
63
  ]"
56
64
  >
57
65
  <BaseAutocompleteDrawer
@@ -86,7 +94,7 @@
86
94
  </template>
87
95
  </BaseAutocompleteDrawer>
88
96
  </div>
89
- </div>
97
+ </Teleport>
90
98
  </div>
91
99
  </template>
92
100
 
@@ -102,6 +110,8 @@ import BaseAutocompleteDrawer from './BaseAutocompleteDrawer.vue';
102
110
  import { twMerge } from 'tailwind-merge';
103
111
  import { t } from '@/i18n';
104
112
  import { Size, sizes } from '@/utils/sizes';
113
+ import { autoUpdate, flip, offset, useFloating } from '@floating-ui/vue';
114
+ import { useElementBounding } from '@vueuse/core';
105
115
 
106
116
  const snackbars = useSnackbarsStore();
107
117
 
@@ -248,6 +258,8 @@ onBeforeUnmount(() => {
248
258
  clearTimeout(openOfFocusTimeout);
249
259
  });
250
260
 
261
+ const inputRef = ref<HTMLElement | null>(null);
262
+ const dropdownRef = ref<HTMLElement | null>(null);
251
263
  const drawer = ref<InstanceType<typeof BaseAutocompleteDrawer> | null>(null);
252
264
 
253
265
  const keywords = ref('');
@@ -286,7 +298,7 @@ const filteredNormalizedOptions = computed((): NormalizedOption[] => {
286
298
 
287
299
  useClickOutside(autocomplete, () => {
288
300
  close();
289
- });
301
+ }, { includes: () => [dropdownRef] });
290
302
 
291
303
  function open() {
292
304
  // Always focus as a safety
@@ -422,6 +434,20 @@ const removeOption = (option: NormalizedOption) => {
422
434
  update(newModelValue);
423
435
  };
424
436
 
437
+ // Floating UI
438
+
439
+ const inputRefRect = useElementBounding(inputRef);
440
+ const inputRefWidth = computed(() => {
441
+ return inputRefRect.width.value;
442
+ });
443
+
444
+ const { floatingStyles } = useFloating(inputRef, dropdownRef, {
445
+ middleware: [offset(4), flip({
446
+ fallbackPlacements: ['right', 'bottom'],
447
+ })],
448
+ whileElementsMounted: autoUpdate,
449
+ });
450
+
425
451
  // Element Classes
426
452
 
427
453
  const wrapperClass = computed(() => {