sprintify-ui 0.6.74 → 0.6.76

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,6 +1,6 @@
1
1
  {
2
2
  "name": "sprintify-ui",
3
- "version": "0.6.74",
3
+ "version": "0.6.76",
4
4
  "scripts": {
5
5
  "build": "rimraf dist && vue-tsc && vite build",
6
6
  "build-fast": "rimraf dist && vite build",
@@ -17,7 +17,7 @@ export default {
17
17
  args: {
18
18
  url: 'https://faker.witify.io/api/todos',
19
19
  field: 'name',
20
- primaryKey: 'uuid',
20
+ primaryKey: 'id',
21
21
  showRouteUrl(id) {
22
22
  return `https://faker.witify.io/api/todos/${id}`;
23
23
  },
@@ -28,7 +28,7 @@ export default {
28
28
  const Template = (args) => ({
29
29
  components: { BaseBelongsToFetch, ShowValue },
30
30
  setup() {
31
- const value = ref(null);
31
+ const value = ref(4);
32
32
  return { args, value };
33
33
  },
34
34
  template: `
@@ -78,9 +78,6 @@ export const Sizes = (args) => ({
78
78
 
79
79
  export const Disabled = Template.bind({});
80
80
  Disabled.args = {
81
- currentModel: options[0],
82
- primaryKey: 'value',
83
- field: 'label',
84
81
  disabled: true,
85
82
  };
86
83
 
@@ -107,7 +104,7 @@ export const SlotOption = (args) => {
107
104
  }"
108
105
  >
109
106
  <p class="text-sm font-medium">{{ option.title }}</p>
110
- <p class="opacity-60 text-xs">{{ option.owner?.name }}</p>
107
+ <p class="opacity-60 text-xs">{{ option.description }}</p>
111
108
  </div>
112
109
  </template>
113
110
  </BaseBelongsToFetch>
@@ -169,8 +166,8 @@ export const WithSelect = (args) => {
169
166
  return {
170
167
  components: { BaseBelongsToFetch, ShowValue },
171
168
  setup() {
172
- const value = ref(options[0]);
173
169
  const selected = ref(null);
170
+ const value = ref(5);
174
171
 
175
172
  const select = {
176
173
  options: [
@@ -49,7 +49,7 @@ import { config } from '@/index';
49
49
  import BaseAutocompleteFetch from './BaseAutocompleteFetch.vue';
50
50
  import { Option, SelectConfiguration } from '@/types';
51
51
  import { Size } from '@/utils/sizes';
52
- import { isObject } from 'lodash';
52
+ import { debounce, isObject } from 'lodash';
53
53
 
54
54
  const props = defineProps({
55
55
  modelValue: {
@@ -144,62 +144,21 @@ const autocompleteFetch = ref<InstanceType<
144
144
  typeof BaseAutocompleteFetch
145
145
  > | null>(null);
146
146
 
147
- const model = ref(props.currentModel);
147
+ const model = ref<Option | null>(null);
148
+ const ensureModelIsFilledDebounced = debounce(ensureModelIsFilled, 100);
148
149
 
149
150
  watch(
150
151
  () => props.currentModel,
151
- (newValue, oldValue) => {
152
- model.value = newValue;
153
- },
154
- { deep: true }
152
+ ensureModelIsFilledDebounced,
155
153
  );
156
154
 
157
155
  watch(
158
156
  () => props.modelValue,
159
- (newValue, oldValue) => {
160
-
161
- if (props.showRouteUrl == null) {
162
- return;
163
- }
164
-
165
- if (props.currentModel !== undefined) {
166
- return;
167
- }
168
-
169
- if (!props.modelValue) {
170
- model.value = null;
171
- return;
172
- }
173
-
174
- if (newValue == oldValue) {
175
- return;
176
- }
177
-
178
- http
179
- .get(props.showRouteUrl(props.modelValue))
180
- .then((response: AxiosResponse) => {
181
-
182
- const data = response.data as Record<string, unknown>;
183
-
184
- if (!isObject(data)) {
185
- return;
186
- }
187
-
188
- if (data[props.primaryKey as never] == props.modelValue) {
189
- model.value = data;
190
- return;
191
- }
192
-
193
- if (isObject(data.data) && data.data[props.primaryKey as never] == props.modelValue) {
194
- model.value = data.data;
195
- return;
196
- }
197
- })
198
- .catch((e: Error) => e);
199
- },
200
- { immediate: true }
157
+ ensureModelIsFilledDebounced,
201
158
  );
202
159
 
160
+ ensureModelIsFilledDebounced();
161
+
203
162
  function onUpdate(newModel: Option | null) {
204
163
  if (!newModel) {
205
164
  model.value = null;
@@ -210,6 +169,54 @@ function onUpdate(newModel: Option | null) {
210
169
  }
211
170
  }
212
171
 
172
+ function ensureModelIsFilled() {
173
+
174
+ if (props.modelValue == null) {
175
+ model.value = null;
176
+ return;
177
+ }
178
+
179
+ const modelId = model.value ? model.value[props.primaryKey as never] : null;
180
+
181
+ // Current value is already set
182
+ if (props.modelValue == modelId) {
183
+ return;
184
+ }
185
+
186
+ // Try with current model
187
+ if (props.currentModel && props.currentModel[props.primaryKey as never] == props.modelValue) {
188
+ model.value = props.currentModel;
189
+ return;
190
+ }
191
+
192
+ // Try with show route
193
+
194
+ if (props.showRouteUrl == null) {
195
+ return;
196
+ }
197
+
198
+ http
199
+ .get(props.showRouteUrl(props.modelValue))
200
+ .then((response: AxiosResponse) => {
201
+
202
+ const data = response.data as Record<string, unknown>;
203
+
204
+ if (!isObject(data)) {
205
+ return;
206
+ }
207
+
208
+ if (data[props.primaryKey as never] == props.modelValue) {
209
+ model.value = data;
210
+ return;
211
+ }
212
+
213
+ if (isObject(data.data) && data.data[props.primaryKey as never] == props.modelValue) {
214
+ model.value = data.data;
215
+ return;
216
+ }
217
+ });
218
+ }
219
+
213
220
  defineExpose({
214
221
  focus: () => autocompleteFetch.value?.focus(),
215
222
  blur: () => autocompleteFetch.value?.blur(),
@@ -36,12 +36,17 @@ const Template = (args) => {
36
36
  "4",
37
37
  "6",
38
38
  ]);
39
- return { args, value };
39
+ const currentModels = ref([
40
+ { id: 4, name: 'Todo 4 (local)' },
41
+ { id: 6, name: 'Todo 6 (local)' },
42
+ ])
43
+ return { args, value, currentModels };
40
44
  },
41
45
  template: `
42
46
  <BaseHasMany
43
47
  v-model="value"
44
48
  v-bind="args"
49
+ :current-models="currentModels"
45
50
  ></BaseHasMany>
46
51
  <ShowValue :value="value" />
47
52
  <BaseAppNotifications />
@@ -56,8 +61,9 @@ export const Disabled = (args) => {
56
61
  return {
57
62
  components: { BaseHasMany, ShowValue },
58
63
  setup() {
64
+ // current model is incorrect, to test component's resilience
59
65
  const currentModel = options[1];
60
- const value = ref([currentModel.value]);
66
+ const value = ref([7]);
61
67
  return { args, value, currentModel };
62
68
  },
63
69
  template: `<BaseHasMany
@@ -65,8 +71,6 @@ export const Disabled = (args) => {
65
71
  v-model="value"
66
72
  :current-models="[currentModel]"
67
73
  :disabled="true"
68
- primaryKey="value"
69
- field="label"
70
74
  ></BaseHasMany>
71
75
  <ShowValue :value="value" />`,
72
76
  };
@@ -44,7 +44,7 @@
44
44
  </template>
45
45
 
46
46
  <script lang="ts" setup>
47
- import { debounce, isEqual } from 'lodash';
47
+ import { debounce } from 'lodash';
48
48
  import { Option } from '@/types';
49
49
  import { config } from '@/index';
50
50
  import { PropType } from 'vue';
@@ -113,74 +113,98 @@ const tagAutocompleteFetch = ref<InstanceType<
113
113
  typeof BaseTagAutocompleteFetch
114
114
  > | null>(null);
115
115
 
116
- const models = ref(props.currentModels ?? []);
116
+ const models = ref<Option[]>([]);
117
+ const ensureModelIsFilledDebounced = debounce(() => ensureModelsAreFilled(), 100);
117
118
 
118
119
  watch(
119
120
  () => props.currentModels,
120
- (newValue, oldValue) => {
121
- if (isEqual(newValue, oldValue)) {
122
- return;
123
- }
124
-
125
- models.value = newValue ?? [];
126
- },
121
+ ensureModelIsFilledDebounced,
127
122
  { deep: true }
128
123
  );
129
124
 
130
125
  watch(
131
126
  () => props.modelValue,
132
- debounce(() => fetchModels(), 100),
133
- { immediate: true }
127
+ ensureModelIsFilledDebounced,
128
+ { deep: true }
134
129
  );
135
130
 
136
- function fetchModels() {
131
+ ensureModelIsFilledDebounced();
137
132
 
138
- if (props.currentModels !== undefined) {
139
- return;
140
- }
133
+ function onUpdate(newModels: Option[]) {
134
+ models.value = newModels;
135
+ emit(
136
+ 'update:modelValue',
137
+ newModels.map((m) => m[props.primaryKey]),
138
+ newModels,
139
+ );
140
+ }
141
141
 
142
- if (props.showRouteUrl == undefined) {
142
+ function ensureModelsAreFilled() {
143
+
144
+ if (!Array.isArray(props.modelValue)) {
145
+ models.value = [];
143
146
  return;
144
147
  }
145
148
 
146
- if (!props.modelValue) {
149
+ if (props.modelValue.length == 0) {
147
150
  models.value = [];
148
151
  return;
149
152
  }
150
153
 
151
- // Do not fetch if the modelValue is the same as the local models
154
+ // Remove incorrect models
152
155
 
153
- // Get primaryKeys as string for comparison
154
156
  const ids = props.modelValue.map((id: number | string) => id.toString());
155
- const localModelIds = models.value.map((m) => '' + m[props.primaryKey]);
156
157
 
157
- if (isEqual(ids, localModelIds)) {
158
+ models.value = models.value.filter((m) => ids.includes(m[props.primaryKey] + ''));
159
+
160
+ const localModelIds = models.value.map((m) => m[props.primaryKey] + '');
161
+
162
+ let missingIds = ids.filter((id) => !localModelIds.includes(id));
163
+
164
+
165
+ // Current models are fully set
166
+ if (missingIds.length == 0) {
167
+ return;
168
+ }
169
+
170
+ // Try with current models
171
+
172
+ if (Array.isArray(props.currentModels)) {
173
+ missingIds.forEach((id) => {
174
+ const model = props.currentModels?.find((m) => m[props.primaryKey as never] == id);
175
+
176
+ if (model) {
177
+ models.value.push(model);
178
+ missingIds = missingIds.filter((i) => i != id);
179
+ }
180
+ });
181
+ }
182
+
183
+ // Current models are fully set
184
+ if (missingIds.length == 0) {
185
+ return;
186
+ }
187
+
188
+ // Try with show route
189
+
190
+ if (props.showRouteUrl == null) {
158
191
  return;
159
192
  }
160
193
 
161
194
  http
162
- .get(props.showRouteUrl(ids))
195
+ .get(props.showRouteUrl(missingIds))
163
196
  .then((response: AxiosResponse) => {
164
197
 
165
198
  const items = getItems(response.data);
166
199
 
167
200
  models.value = items.filter((i: Record<string, any>) => {
168
201
  // convert primary keys to string for comparison
169
- return ids.includes('' + i[props.primaryKey]);
202
+ return ids.includes(i[props.primaryKey] + '');
170
203
  });
171
204
  })
172
205
  .catch((e: Error) => e);
173
206
  }
174
207
 
175
- function onUpdate(newModels: Option[]) {
176
- models.value = newModels;
177
- emit(
178
- 'update:modelValue',
179
- newModels.map((m) => m[props.primaryKey]),
180
- newModels,
181
- );
182
- }
183
-
184
208
  defineExpose({
185
209
  focus: () => tagAutocompleteFetch.value?.focus(),
186
210
  blur: () => tagAutocompleteFetch.value?.blur(),
@@ -12,7 +12,10 @@
12
12
  :key="selection.value ? selection.value : 'null'"
13
13
  :class="selectionClass(selection)"
14
14
  >
15
- <div>
15
+ <div
16
+ :title="selection.label"
17
+ class="truncate"
18
+ >
16
19
  {{ selection.label }}
17
20
  </div>
18
21
 
@@ -459,7 +462,7 @@ const inputClasses = computed(() => {
459
462
 
460
463
  const selectionClass = (selection: NormalizedOption): string => {
461
464
 
462
- const base = 'flex items-center rounded border';
465
+ const base = 'flex items-center rounded border overflow-hidden';
463
466
 
464
467
  const fontSize = {
465
468
  'xs': 'text-xs',