sprintify-ui 0.10.87 → 0.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/dist/{BaseCkeditor-Laq0HbvP.js → BaseCkeditor-Dcqohrsz.js} +14 -14
- package/dist/sprintify-ui.es.js +12480 -12553
- package/dist/style.css +2 -2
- package/dist/types/components/BaseActionItem.vue.d.ts +11 -5
- package/dist/types/components/BaseAddressForm.vue.d.ts +1 -1
- package/dist/types/components/BaseAlert.vue.d.ts +3 -21
- package/dist/types/components/BaseApp.vue.d.ts +2 -9
- package/dist/types/components/BaseAutocomplete.vue.d.ts +19 -643
- package/dist/types/components/BaseAutocompleteDrawer.vue.d.ts +21 -104
- package/dist/types/components/BaseAutocompleteFetch.vue.d.ts +26 -1045
- package/dist/types/components/BaseAvatarGroup.vue.d.ts +1 -1
- package/dist/types/components/BaseBadge.vue.d.ts +3 -20
- package/dist/types/components/BaseBelongsTo.vue.d.ts +19 -1022
- package/dist/types/components/BaseBelongsToFetch.vue.d.ts +10 -745
- package/dist/types/components/BaseButton.vue.d.ts +6 -77
- package/dist/types/components/BaseButtonGroup.vue.d.ts +3 -159
- package/dist/types/components/BaseCard.vue.d.ts +2 -23
- package/dist/types/components/BaseCardRow.vue.d.ts +2 -16
- package/dist/types/components/BaseCkeditor.vue.d.ts +1 -1
- package/dist/types/components/BaseClipboard.vue.d.ts +2 -42
- package/dist/types/components/BaseCollapse.vue.d.ts +2 -26
- package/dist/types/components/BaseContainer.vue.d.ts +2 -12
- package/dist/types/components/BaseCounter.vue.d.ts +1 -1
- package/dist/types/components/BaseCropper.vue.d.ts +3 -35
- package/dist/types/components/BaseDataIterator.vue.d.ts +18 -11
- package/dist/types/components/BaseDataIteratorSectionBox.vue.d.ts +2 -10
- package/dist/types/components/BaseDataIteratorSectionModal.vue.d.ts +2 -16
- package/dist/types/components/BaseDataTable.vue.d.ts +54 -2141
- package/dist/types/components/BaseDataTableTemplate.vue.d.ts +2 -584
- package/dist/types/components/BaseDatePicker.vue.d.ts +2 -2
- package/dist/types/components/BaseDescriptionList.vue.d.ts +2 -7
- package/dist/types/components/BaseDescriptionListItem.vue.d.ts +2 -9
- package/dist/types/components/BaseDialog.vue.d.ts +3 -110
- package/dist/types/components/BaseDisplayRelativeTime.vue.d.ts +2 -51
- package/dist/types/components/BaseDraggable.vue.d.ts +2 -18
- package/dist/types/components/BaseDropdown.vue.d.ts +2 -147
- package/dist/types/components/BaseDropdownAutocomplete.vue.d.ts +3 -124
- package/dist/types/components/BaseField.vue.d.ts +3 -99
- package/dist/types/components/BaseFilePicker.vue.d.ts +2 -28
- package/dist/types/components/BaseFilePickerCrop.vue.d.ts +2 -80
- package/dist/types/components/BaseFileUploader.vue.d.ts +3 -173
- package/dist/types/components/BaseForm.vue.d.ts +3 -150
- package/dist/types/components/BaseGantt.vue.d.ts +40 -1141
- package/dist/types/components/BaseHasMany.vue.d.ts +18 -577
- package/dist/types/components/BaseHasManyFetch.vue.d.ts +3 -589
- package/dist/types/components/BaseInput.vue.d.ts +3 -3
- package/dist/types/components/BaseInputError.vue.d.ts +2 -19
- package/dist/types/components/BaseInputPercent.vue.d.ts +1 -1
- package/dist/types/components/BaseLayoutSidebar.vue.d.ts +2 -112
- package/dist/types/components/BaseLayoutSidebarConfigurable.vue.d.ts +2 -113
- package/dist/types/components/BaseLayoutStacked.vue.d.ts +2 -52
- package/dist/types/components/BaseLayoutStackedConfigurable.vue.d.ts +2 -117
- package/dist/types/components/BaseLazy.vue.d.ts +2 -13
- package/dist/types/components/BaseMediaGallery.vue.d.ts +3 -3
- package/dist/types/components/BaseMediaGalleryItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaLibrary.vue.d.ts +3 -207
- package/dist/types/components/BaseMediaList.vue.d.ts +3 -3
- package/dist/types/components/BaseMediaListItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMediaPictures.vue.d.ts +3 -3
- package/dist/types/components/BaseMediaPicturesItem.vue.d.ts +1 -1
- package/dist/types/components/BaseMenu.vue.d.ts +2 -209
- package/dist/types/components/BaseMenuItem.vue.d.ts +1 -1
- package/dist/types/components/BaseModalCenter.vue.d.ts +2 -83
- package/dist/types/components/BaseModalSide.vue.d.ts +2 -80
- package/dist/types/components/BaseNavbar.vue.d.ts +2 -83
- package/dist/types/components/BasePassword.vue.d.ts +1 -1
- package/dist/types/components/BaseRadioGroup.vue.d.ts +2 -107
- package/dist/types/components/BaseReadMore.vue.d.ts +2 -28
- package/dist/types/components/BaseRichText.vue.d.ts +1 -1
- package/dist/types/components/BaseSelect.vue.d.ts +4 -148
- package/dist/types/components/BaseShortcut.vue.d.ts +1 -1
- package/dist/types/components/BaseSideNavigation.vue.d.ts +2 -7
- package/dist/types/components/BaseSideNavigationItem.vue.d.ts +3 -19
- package/dist/types/components/BaseSkeleton.vue.d.ts +1 -1
- package/dist/types/components/BaseSwitch.vue.d.ts +3 -120
- package/dist/types/components/BaseSystemAlert.vue.d.ts +3 -55
- package/dist/types/components/BaseTabItem.vue.d.ts +4 -27
- package/dist/types/components/BaseTable.vue.d.ts +3 -24
- package/dist/types/components/BaseTableBody.vue.d.ts +2 -4
- package/dist/types/components/BaseTableCell.vue.d.ts +6 -36
- package/dist/types/components/BaseTableColumn.vue.d.ts +4 -4
- package/dist/types/components/BaseTableHead.vue.d.ts +2 -9
- package/dist/types/components/BaseTableHeader.vue.d.ts +4 -40
- package/dist/types/components/BaseTableRow.vue.d.ts +4 -38
- package/dist/types/components/BaseTabs.vue.d.ts +2 -16
- package/dist/types/components/BaseTagAutocomplete.vue.d.ts +25 -571
- package/dist/types/components/BaseTagAutocompleteFetch.vue.d.ts +3 -796
- package/dist/types/components/BaseTextarea.vue.d.ts +1 -1
- package/dist/types/components/BaseTextareaAutoresize.vue.d.ts +1 -1
- package/dist/types/components/BaseTimePicker.vue.d.ts +1 -1
- package/dist/types/components/BaseTooltip.vue.d.ts +2 -35
- package/dist/types/services/gantt/types.d.ts +18 -8
- package/package.json +6 -9
- package/src/changelog.mdx +1 -1
- package/src/components/BaseAssign.mdx +1 -1
- package/src/components/BaseAutocomplete.stories.js +10 -0
- package/src/components/BaseAutocomplete.vue +11 -1
- package/src/components/BaseAutocompleteDrawer.vue +52 -1
- package/src/components/BaseAutocompleteFetch.stories.js +6 -0
- package/src/components/BaseAutocompleteFetch.vue +15 -0
- package/src/components/BaseBelongsTo.stories.js +5 -0
- package/src/components/BaseBelongsTo.vue +10 -0
- package/src/components/BaseBelongsToFetch.stories.js +6 -0
- package/src/components/BaseBelongsToFetch.vue +9 -0
- package/src/components/BaseForm.mdx +1 -1
- package/src/components/BaseGantt.mdx +79 -0
- package/src/components/BaseGantt.stories.js +25 -10
- package/src/components/BaseGantt.vue +192 -58
- package/src/components/BaseHasMany.stories.js +5 -0
- package/src/components/BaseHasMany.vue +10 -0
- package/src/components/BaseHasManyFetch.stories.js +12 -0
- package/src/components/BaseTagAutocomplete.stories.js +5 -0
- package/src/components/BaseTagAutocomplete.vue +34 -1
- package/src/components/BaseTagAutocompleteFetch.stories.js +6 -0
- package/src/services/gantt/format.ts +30 -7
- package/src/services/gantt/types.ts +18 -8
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<!-- Sidebar -->
|
|
9
9
|
<div
|
|
10
10
|
class="border-r border-slate-300 relative shrink-0"
|
|
11
|
-
:style="{
|
|
11
|
+
:style="{ width: `${props.sidebarWidth}px` }"
|
|
12
12
|
>
|
|
13
13
|
<!-- Top-left Corner-->
|
|
14
14
|
<div
|
|
@@ -30,22 +30,68 @@
|
|
|
30
30
|
<li
|
|
31
31
|
v-for="row in rowsInternal"
|
|
32
32
|
:key="row.id"
|
|
33
|
-
class="border-b border-slate-300
|
|
34
|
-
:style="{
|
|
35
|
-
height: `${props.rowHeight}px`,
|
|
36
|
-
}"
|
|
37
|
-
@click="$emit('row:click', row)"
|
|
33
|
+
class="block border-b border-slate-300 last:border-none"
|
|
38
34
|
>
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
<div
|
|
36
|
+
:style="{
|
|
37
|
+
height: row.height + 'px',
|
|
38
|
+
}"
|
|
39
|
+
class="w-full flex overflow-hidden"
|
|
42
40
|
>
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
class="p-1 shrink-0 block"
|
|
44
|
+
@click="toggleRow(row.id)"
|
|
45
|
+
>
|
|
46
|
+
<BaseIcon
|
|
47
|
+
icon="mdi:chevron-down"
|
|
48
|
+
:class="{
|
|
49
|
+
'rotate-0': isRowExpanded(row.id),
|
|
50
|
+
'-rotate-90': !isRowExpanded(row.id),
|
|
51
|
+
}"
|
|
52
|
+
class="transition-transform duration-200"
|
|
53
|
+
/>
|
|
54
|
+
</button>
|
|
55
|
+
<div
|
|
56
|
+
class="w-full overflow-hidden grow"
|
|
57
|
+
@click="$emit('row:click', row)"
|
|
58
|
+
>
|
|
59
|
+
<slot
|
|
60
|
+
name="sidebarRow"
|
|
61
|
+
:row="row"
|
|
62
|
+
>
|
|
63
|
+
<div class="px-1 py-1 h-full flex items-center">
|
|
64
|
+
<p class="font-semibold leading-tight truncate text-xs">
|
|
65
|
+
{{ row.name }}
|
|
66
|
+
</p>
|
|
67
|
+
</div>
|
|
68
|
+
</slot>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
<template
|
|
72
|
+
v-if="isRowExpanded(row.id)"
|
|
73
|
+
>
|
|
74
|
+
<div
|
|
75
|
+
v-for="item in row.items"
|
|
76
|
+
:key="item.id"
|
|
77
|
+
:style="{
|
|
78
|
+
height: item.height + 'px'
|
|
79
|
+
}"
|
|
80
|
+
class="w-full border-t overflow-hidden"
|
|
81
|
+
@click="$emit('item:click', item)"
|
|
82
|
+
>
|
|
83
|
+
<slot
|
|
84
|
+
name="sidebarItem"
|
|
85
|
+
:item="item"
|
|
86
|
+
>
|
|
87
|
+
<div class="pr-1 pl-4 py-1 h-full flex items-center">
|
|
88
|
+
<p class="leading-tight truncate text-slate-600 text-xs">
|
|
89
|
+
{{ item.name }}
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
</slot>
|
|
47
93
|
</div>
|
|
48
|
-
</
|
|
94
|
+
</template>
|
|
49
95
|
</li>
|
|
50
96
|
</ul>
|
|
51
97
|
</div>
|
|
@@ -138,49 +184,95 @@
|
|
|
138
184
|
|
|
139
185
|
<ul
|
|
140
186
|
ref="itemsRef"
|
|
141
|
-
class="relative w-full overflow-scroll grow"
|
|
187
|
+
class="relative block w-full overflow-scroll grow"
|
|
142
188
|
>
|
|
143
189
|
<li
|
|
144
190
|
v-for="row in rowsInternal"
|
|
145
191
|
:key="row.id"
|
|
146
|
-
class="border-b relative border-slate-300 last:border-none"
|
|
147
192
|
:style="{
|
|
148
|
-
height: `${props.rowHeight}px`,
|
|
149
193
|
width: `${width}px`,
|
|
150
194
|
}"
|
|
195
|
+
class="border-b border-slate-300 w-full"
|
|
151
196
|
>
|
|
152
|
-
<
|
|
153
|
-
|
|
154
|
-
:key="item.id"
|
|
155
|
-
type="button"
|
|
156
|
-
class="absolute flex"
|
|
197
|
+
<div
|
|
198
|
+
class="block relative"
|
|
157
199
|
:style="{
|
|
158
|
-
|
|
159
|
-
height: item.height + 'px',
|
|
160
|
-
width: item.width + 'px',
|
|
200
|
+
height: `${row.height}px`,
|
|
161
201
|
}"
|
|
162
|
-
:title="`${item.name} - ${item.start.toFormat('yyyy-MM-dd HH:mm:ss')} - ${item.end.toFormat('yyyy-MM-dd HH:mm:ss')}`"
|
|
163
|
-
@click="$emit('item:click', item)"
|
|
164
202
|
>
|
|
165
|
-
<
|
|
166
|
-
|
|
167
|
-
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
class="absolute flex"
|
|
206
|
+
:style="{
|
|
207
|
+
transform: `translate(${row.x}px, ${row.y}px)`,
|
|
208
|
+
height: row.barHeight + 'px',
|
|
209
|
+
width: row.width + 'px',
|
|
210
|
+
}"
|
|
211
|
+
:title="`${row.name} - ${row.start.toFormat('yyyy-MM-dd HH:mm:ss')} - ${row.end.toFormat('yyyy-MM-dd HH:mm:ss')}`"
|
|
212
|
+
@click="$emit('row:click', row)"
|
|
168
213
|
>
|
|
169
|
-
<
|
|
214
|
+
<slot
|
|
215
|
+
name="row"
|
|
216
|
+
:row="row"
|
|
217
|
+
>
|
|
218
|
+
<div
|
|
219
|
+
:style="{
|
|
220
|
+
backgroundColor: 'gray',
|
|
221
|
+
}"
|
|
222
|
+
class="flex w-full h-full items-center rounded hover:opacity-80 duration-200"
|
|
223
|
+
>
|
|
224
|
+
<p
|
|
225
|
+
class="text-white text-xs px-2 py-1 truncate"
|
|
226
|
+
style="text-shadow: 0.5px 0.5px rgba(0,0,0,0.1);"
|
|
227
|
+
>
|
|
228
|
+
{{ row.name }}
|
|
229
|
+
</p>
|
|
230
|
+
</div>
|
|
231
|
+
</slot>
|
|
232
|
+
</button>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<div v-if="isRowExpanded(row.id)">
|
|
236
|
+
<div
|
|
237
|
+
v-for="item in row.items"
|
|
238
|
+
:key="row.id + '-' + item.id"
|
|
239
|
+
class="block border-t relative border-slate-200 w-full"
|
|
240
|
+
:style="{
|
|
241
|
+
height: `${item.height}px`,
|
|
242
|
+
}"
|
|
243
|
+
>
|
|
244
|
+
<button
|
|
245
|
+
type="button"
|
|
246
|
+
class="absolute flex"
|
|
170
247
|
:style="{
|
|
171
|
-
|
|
248
|
+
transform: `translate(${item.x}px, ${item.y}px)`,
|
|
249
|
+
height: item.barHeight + 'px',
|
|
250
|
+
width: item.width + 'px',
|
|
172
251
|
}"
|
|
173
|
-
|
|
252
|
+
:title="`${item.name} - ${item.start.toFormat('yyyy-MM-dd HH:mm:ss')} - ${item.end.toFormat('yyyy-MM-dd HH:mm:ss')}`"
|
|
253
|
+
@click="$emit('item:click', item)"
|
|
174
254
|
>
|
|
175
|
-
<
|
|
176
|
-
|
|
177
|
-
|
|
255
|
+
<slot
|
|
256
|
+
name="item"
|
|
257
|
+
:item="item"
|
|
178
258
|
>
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
259
|
+
<div
|
|
260
|
+
:style="{
|
|
261
|
+
backgroundColor: item.color,
|
|
262
|
+
}"
|
|
263
|
+
class="flex w-full h-full items-center rounded hover:opacity-80 duration-200"
|
|
264
|
+
>
|
|
265
|
+
<p
|
|
266
|
+
class="text-white text-xs px-2 py-1 truncate"
|
|
267
|
+
style="text-shadow: 0.5px 0.5px rgba(0,0,0,0.1);"
|
|
268
|
+
>
|
|
269
|
+
{{ item.name }}
|
|
270
|
+
</p>
|
|
271
|
+
</div>
|
|
272
|
+
</slot>
|
|
273
|
+
</button>
|
|
274
|
+
</div>
|
|
275
|
+
</div>
|
|
184
276
|
</li>
|
|
185
277
|
</ul>
|
|
186
278
|
|
|
@@ -264,31 +356,55 @@
|
|
|
264
356
|
<script lang="ts" setup>
|
|
265
357
|
import { useElementSize, useScroll } from '@vueuse/core';
|
|
266
358
|
import { Format } from '@/services/gantt/format';
|
|
267
|
-
import { FormatConfig, GanttRow, GanttRowFormatted, Group, Tick, NowLine } from '@/services/gantt/types';
|
|
268
|
-
import { debounce } from 'lodash';
|
|
359
|
+
import { FormatConfig, GanttRow, GanttRowFormatted, Group, Tick, NowLine, GanttItemFormatted } from '@/services/gantt/types';
|
|
360
|
+
import { cloneDeep, debounce } from 'lodash';
|
|
269
361
|
import { slate } from 'tailwindcss/colors';
|
|
362
|
+
import { VNode } from 'vue';
|
|
363
|
+
|
|
364
|
+
interface Props {
|
|
365
|
+
/**
|
|
366
|
+
* Rows to display in the Gantt chart
|
|
367
|
+
*/
|
|
368
|
+
rows: GanttRow[];
|
|
369
|
+
/**
|
|
370
|
+
* Width of the sidebar in pixels
|
|
371
|
+
*/
|
|
372
|
+
sidebarWidth?: number;
|
|
373
|
+
/**
|
|
374
|
+
* Height of each row in pixels
|
|
375
|
+
*/
|
|
376
|
+
rowHeight?: number;
|
|
377
|
+
/**
|
|
378
|
+
* Maximum height of the Gantt chart in pixels. The chart will be scrollable if the content exceeds this height.
|
|
379
|
+
*/
|
|
380
|
+
maxHeight?: number;
|
|
381
|
+
/**
|
|
382
|
+
* Whether to include a "now" line in the Gantt chart
|
|
383
|
+
*/
|
|
384
|
+
includeToday?: boolean;
|
|
385
|
+
}
|
|
270
386
|
|
|
271
|
-
const props = withDefaults(defineProps<{
|
|
272
|
-
|
|
273
|
-
rowHeight?: number,
|
|
274
|
-
rowPadding?: number,
|
|
275
|
-
maxHeight?: number,
|
|
276
|
-
includeToday?: boolean,
|
|
277
|
-
}>(), {
|
|
387
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
388
|
+
sidebarWidth: 150,
|
|
278
389
|
rowHeight: 40,
|
|
279
|
-
rowPadding: 4,
|
|
280
390
|
maxHeight: undefined,
|
|
281
391
|
includeToday: true,
|
|
282
392
|
});
|
|
283
393
|
|
|
284
|
-
defineEmits
|
|
285
|
-
'
|
|
286
|
-
'
|
|
287
|
-
|
|
394
|
+
defineEmits<{
|
|
395
|
+
(e: 'row:click', row: GanttRowFormatted): void;
|
|
396
|
+
(e: 'item:click', item: GanttItemFormatted): void;
|
|
397
|
+
}>();
|
|
398
|
+
|
|
399
|
+
defineSlots<{
|
|
400
|
+
sidebarRow: (props: { row: GanttRowFormatted }) => VNode[];
|
|
401
|
+
sidebarItem: (props: { item: GanttItemFormatted }) => VNode[];
|
|
402
|
+
row: (props: { row: GanttRowFormatted }) => VNode[];
|
|
403
|
+
item: (props: { item: GanttItemFormatted }) => VNode[];
|
|
404
|
+
}>();
|
|
288
405
|
|
|
289
406
|
// Config
|
|
290
407
|
|
|
291
|
-
const SIDEBAR_WIDTH = 120;
|
|
292
408
|
const HEADER_HEIGHT = 40;
|
|
293
409
|
|
|
294
410
|
// Init
|
|
@@ -308,9 +424,9 @@ const nowLine = ref<NowLine | null>(null);
|
|
|
308
424
|
const config = computed<FormatConfig>(() => {
|
|
309
425
|
return {
|
|
310
426
|
rows: props.rows,
|
|
427
|
+
sidebarWidth: props.sidebarWidth,
|
|
311
428
|
minWidth: contentSize.width.value,
|
|
312
429
|
rowHeight: props.rowHeight,
|
|
313
|
-
rowPadding: props.rowPadding,
|
|
314
430
|
includeToday: props.includeToday,
|
|
315
431
|
};
|
|
316
432
|
});
|
|
@@ -328,6 +444,8 @@ function init() {
|
|
|
328
444
|
|
|
329
445
|
nowLine.value = format.nowLine;
|
|
330
446
|
|
|
447
|
+
expandedRows.value = new Set(props.rows.map((r) => r.id));
|
|
448
|
+
|
|
331
449
|
// Scroll to now line
|
|
332
450
|
|
|
333
451
|
nextTick(() => {
|
|
@@ -370,7 +488,7 @@ const currentGroup = computed<Group | undefined>(() => {
|
|
|
370
488
|
|
|
371
489
|
const offsetLeft = -scrollX.value;
|
|
372
490
|
|
|
373
|
-
return groups.value
|
|
491
|
+
return cloneDeep(groups.value)
|
|
374
492
|
// Sort by x descending
|
|
375
493
|
.sort((a, b) => {
|
|
376
494
|
return b.x - a.x;
|
|
@@ -381,4 +499,20 @@ const currentGroup = computed<Group | undefined>(() => {
|
|
|
381
499
|
});
|
|
382
500
|
});
|
|
383
501
|
|
|
502
|
+
// Toggle items
|
|
503
|
+
|
|
504
|
+
const expandedRows = ref<Set<string | number>>(new Set());
|
|
505
|
+
|
|
506
|
+
function toggleRow(rowId: string | number) {
|
|
507
|
+
if (expandedRows.value.has(rowId)) {
|
|
508
|
+
expandedRows.value.delete(rowId);
|
|
509
|
+
} else {
|
|
510
|
+
expandedRows.value.add(rowId);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function isRowExpanded(rowId: string | number): boolean {
|
|
515
|
+
return expandedRows.value.has(rowId);
|
|
516
|
+
}
|
|
517
|
+
|
|
384
518
|
</script>
|
|
@@ -16,6 +16,11 @@ export default {
|
|
|
16
16
|
options: options,
|
|
17
17
|
field: "label",
|
|
18
18
|
primaryKey: "value",
|
|
19
|
+
optionColor: (option) => {
|
|
20
|
+
if (option.type === "jedi") return "blue";
|
|
21
|
+
if (option.type === "sith") return "black";
|
|
22
|
+
return "gray";
|
|
23
|
+
}
|
|
19
24
|
},
|
|
20
25
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
21
26
|
};
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
:size="size"
|
|
13
13
|
:has-error="hasError"
|
|
14
14
|
:max="max"
|
|
15
|
+
:option-color="optionColor"
|
|
16
|
+
:option-icon="optionIcon"
|
|
15
17
|
@update:model-value="onUpdate"
|
|
16
18
|
>
|
|
17
19
|
<template #items="itemProps">
|
|
@@ -96,6 +98,14 @@ const props = defineProps({
|
|
|
96
98
|
default: false,
|
|
97
99
|
type: Boolean,
|
|
98
100
|
},
|
|
101
|
+
optionColor: {
|
|
102
|
+
default: undefined,
|
|
103
|
+
type: Function as PropType<(option: RawOption) => string>,
|
|
104
|
+
},
|
|
105
|
+
optionIcon: {
|
|
106
|
+
default: undefined,
|
|
107
|
+
type: Function as PropType<(option: RawOption) => string>,
|
|
108
|
+
},
|
|
99
109
|
});
|
|
100
110
|
|
|
101
111
|
const emit = defineEmits(['update:modelValue']);
|
|
@@ -22,6 +22,18 @@ export default {
|
|
|
22
22
|
const params = QueryString.stringify({ filter: { id: ids } });
|
|
23
23
|
return `https://faker.witify.io/api/todos?${params}`;
|
|
24
24
|
},
|
|
25
|
+
optionColor: (option) => {
|
|
26
|
+
if (option.type === "work") return "green";
|
|
27
|
+
if (option.type === "personal") return "blue";
|
|
28
|
+
if (option.type === "family") return "purple";
|
|
29
|
+
return "gray";
|
|
30
|
+
},
|
|
31
|
+
optionIcon: (option) => {
|
|
32
|
+
if (option.type === "work") return "heroicons-solid:briefcase";
|
|
33
|
+
if (option.type === "personal") return "heroicons-solid:user-circle";
|
|
34
|
+
if (option.type === "family") return "heroicons-solid:home";
|
|
35
|
+
return "heroicons-solid:tag";
|
|
36
|
+
}
|
|
25
37
|
},
|
|
26
38
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
27
39
|
};
|
|
@@ -22,6 +22,11 @@ export default {
|
|
|
22
22
|
labelKey: "label",
|
|
23
23
|
valueKey: "value",
|
|
24
24
|
options: options,
|
|
25
|
+
optionColor: (option) => {
|
|
26
|
+
if (option.type === "jedi") return "blue";
|
|
27
|
+
if (option.type === "sith") return "black";
|
|
28
|
+
return "gray";
|
|
29
|
+
}
|
|
25
30
|
},
|
|
26
31
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
27
32
|
};
|
|
@@ -17,8 +17,30 @@
|
|
|
17
17
|
>
|
|
18
18
|
<div
|
|
19
19
|
:title="selection.label"
|
|
20
|
-
class="truncate"
|
|
20
|
+
class="truncate flex gap-2 items-center"
|
|
21
21
|
>
|
|
22
|
+
<div
|
|
23
|
+
v-if="optionIcon && selection.option"
|
|
24
|
+
class="shrink-0"
|
|
25
|
+
>
|
|
26
|
+
<BaseIcon
|
|
27
|
+
:icon="optionIcon(selection.option)"
|
|
28
|
+
class="shrink-0"
|
|
29
|
+
:color="optionColor ? optionColor(selection.option) : 'gray'"
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
<div
|
|
33
|
+
v-else-if="optionColor && selection.option"
|
|
34
|
+
class="shrink-0"
|
|
35
|
+
>
|
|
36
|
+
<div
|
|
37
|
+
class="shrink-0 rounded-full h-2.5 w-2.5"
|
|
38
|
+
:style="{
|
|
39
|
+
backgroundColor: optionColor(selection.option),
|
|
40
|
+
}"
|
|
41
|
+
/>
|
|
42
|
+
</div>
|
|
43
|
+
|
|
22
44
|
{{ selection.label }}
|
|
23
45
|
</div>
|
|
24
46
|
|
|
@@ -70,6 +92,8 @@
|
|
|
70
92
|
:loading="loading"
|
|
71
93
|
:loading-bottom="loadingBottom"
|
|
72
94
|
tw-drawer="p-1"
|
|
95
|
+
:option-color="optionColor"
|
|
96
|
+
:option-icon="optionIcon"
|
|
73
97
|
:keywords="keywords"
|
|
74
98
|
@select="onSelect"
|
|
75
99
|
@scroll-bottom="emit('scrollBottom')"
|
|
@@ -112,6 +136,7 @@ import { t } from '@/i18n';
|
|
|
112
136
|
import { Size, sizes } from '@/utils/sizes';
|
|
113
137
|
import { autoUpdate, flip, offset, useFloating } from '@floating-ui/vue';
|
|
114
138
|
import { useElementBounding } from '@vueuse/core';
|
|
139
|
+
import { BaseIcon } from '.';
|
|
115
140
|
|
|
116
141
|
const snackbars = useSnackbarsStore();
|
|
117
142
|
|
|
@@ -193,6 +218,14 @@ const props = defineProps({
|
|
|
193
218
|
default: true,
|
|
194
219
|
type: Boolean,
|
|
195
220
|
},
|
|
221
|
+
optionColor: {
|
|
222
|
+
default: undefined,
|
|
223
|
+
type: Function as PropType<(option: RawOption) => string>,
|
|
224
|
+
},
|
|
225
|
+
optionIcon: {
|
|
226
|
+
default: undefined,
|
|
227
|
+
type: Function as PropType<(option: RawOption) => string>,
|
|
228
|
+
},
|
|
196
229
|
});
|
|
197
230
|
|
|
198
231
|
const emit = defineEmits([
|
|
@@ -16,6 +16,12 @@ export default {
|
|
|
16
16
|
url: "https://faker.witify.io/api/todos",
|
|
17
17
|
labelKey: "name",
|
|
18
18
|
valueKey: "id",
|
|
19
|
+
optionColor: (option) => {
|
|
20
|
+
if (option.type === "work") return "green";
|
|
21
|
+
if (option.type === "personal") return "blue";
|
|
22
|
+
if (option.type === "family") return "purple";
|
|
23
|
+
return "gray";
|
|
24
|
+
},
|
|
19
25
|
},
|
|
20
26
|
decorators: [() => ({ template: '<div class="mb-36"><story/></div>' })],
|
|
21
27
|
};
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
2
|
import { FormatConfig, GanttItem, GanttItemFormatted, GanttRow, GanttRowFormatted, NowLine } from "./types";
|
|
3
3
|
import { Timescale } from "./timescale";
|
|
4
|
+
import { sum } from "lodash";
|
|
5
|
+
|
|
6
|
+
const PADDING_Y_ROW = 4;
|
|
4
7
|
|
|
5
8
|
export class Format {
|
|
6
9
|
|
|
@@ -35,22 +38,36 @@ export class Format {
|
|
|
35
38
|
row.items.forEach((item) => {
|
|
36
39
|
|
|
37
40
|
const x = (item.start.toMillis() - min.toMillis()) * millisecondToPixel;
|
|
38
|
-
const y =
|
|
41
|
+
const y = PADDING_Y_ROW;
|
|
39
42
|
const width = item.milliseconds * millisecondToPixel;
|
|
40
|
-
const height =
|
|
43
|
+
const height = item.height - PADDING_Y_ROW * 2;
|
|
41
44
|
|
|
42
45
|
item.x = x;
|
|
43
46
|
item.y = y;
|
|
44
47
|
item.width = width;
|
|
45
|
-
item.
|
|
48
|
+
item.barHeight = height;
|
|
46
49
|
});
|
|
50
|
+
|
|
51
|
+
const x = Math.min(...row.items.map((item) => item.x));
|
|
52
|
+
const y = PADDING_Y_ROW;
|
|
53
|
+
const height = row.height - (PADDING_Y_ROW * 2);
|
|
54
|
+
const width = Math.max(...row.items.map((item) => item.x + item.width)) - x;
|
|
55
|
+
const start = DateTime.min(...row.items.map((item) => item.start)) as DateTime;
|
|
56
|
+
const end = DateTime.max(...row.items.map((item) => item.end)) as DateTime;
|
|
57
|
+
|
|
58
|
+
row.x = x;
|
|
59
|
+
row.y = y;
|
|
60
|
+
row.barHeight = height;
|
|
61
|
+
row.width = width;
|
|
62
|
+
row.start = start;
|
|
63
|
+
row.end = end;
|
|
47
64
|
});
|
|
48
65
|
|
|
49
66
|
const today = DateTime.local().startOf(timescale.scale.tick.step);
|
|
50
67
|
|
|
51
68
|
let nowLine = null;
|
|
52
69
|
|
|
53
|
-
if (today >= min && today <= max) {
|
|
70
|
+
if (today >= min && today <= max && this.config.includeToday) {
|
|
54
71
|
nowLine = {
|
|
55
72
|
x: (today.toMillis() - min.toMillis()) * millisecondToPixel,
|
|
56
73
|
} as NowLine;
|
|
@@ -61,7 +78,7 @@ export class Format {
|
|
|
61
78
|
min,
|
|
62
79
|
max,
|
|
63
80
|
millisecondToPixel,
|
|
64
|
-
height:
|
|
81
|
+
height: sum(rowsFormatted.map((r) => r.height + sum(r.items.map((i) => i.height)))),
|
|
65
82
|
width: timescale.width,
|
|
66
83
|
groups: timescale.groups,
|
|
67
84
|
ticks: timescale.ticks,
|
|
@@ -85,7 +102,13 @@ export class Format {
|
|
|
85
102
|
name: row.name,
|
|
86
103
|
meta: row.meta,
|
|
87
104
|
items: itemsFormatted,
|
|
88
|
-
|
|
105
|
+
height: row.height ?? this.config.rowHeight,
|
|
106
|
+
width: 0,
|
|
107
|
+
start: DateTime.now(),
|
|
108
|
+
end: DateTime.now(),
|
|
109
|
+
x: 0,
|
|
110
|
+
y: 0,
|
|
111
|
+
} as GanttRowFormatted;
|
|
89
112
|
}
|
|
90
113
|
|
|
91
114
|
formatGanttItem(item: GanttItem): GanttItemFormatted {
|
|
@@ -104,7 +127,7 @@ export class Format {
|
|
|
104
127
|
x: 0,
|
|
105
128
|
y: 0,
|
|
106
129
|
width: 0,
|
|
107
|
-
height:
|
|
130
|
+
height: item.height ?? this.config.rowHeight,
|
|
108
131
|
} as GanttItemFormatted;
|
|
109
132
|
}
|
|
110
133
|
|
|
@@ -4,6 +4,7 @@ export interface GanttItem {
|
|
|
4
4
|
id: number | string;
|
|
5
5
|
start: string;
|
|
6
6
|
end: string;
|
|
7
|
+
height?: number;
|
|
7
8
|
name: string;
|
|
8
9
|
meta?: Record<string, unknown>;
|
|
9
10
|
color: string;
|
|
@@ -14,34 +15,43 @@ export interface GanttRow {
|
|
|
14
15
|
name: string;
|
|
15
16
|
meta?: Record<string, unknown>;
|
|
16
17
|
items: GanttItem[];
|
|
18
|
+
height?: number;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface GanttRowFormatted {
|
|
20
22
|
id: number | string;
|
|
21
23
|
name: string;
|
|
22
|
-
meta?: Record<string, unknown>;
|
|
23
24
|
items: GanttItemFormatted[];
|
|
25
|
+
height: number;
|
|
26
|
+
barHeight: number;
|
|
27
|
+
width: number;
|
|
28
|
+
start: DateTime;
|
|
29
|
+
end: DateTime;
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
meta?: Record<string, unknown>;
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
export interface GanttItemFormatted {
|
|
27
36
|
id: number | string;
|
|
28
|
-
start: DateTime;
|
|
29
|
-
end: DateTime;
|
|
30
37
|
name: string;
|
|
31
|
-
meta?: Record<string, unknown>;
|
|
32
38
|
color: string;
|
|
33
|
-
|
|
39
|
+
height: number;
|
|
40
|
+
barHeight: number;
|
|
41
|
+
width: number;
|
|
42
|
+
start: DateTime;
|
|
43
|
+
end: DateTime;
|
|
34
44
|
x: number;
|
|
35
45
|
y: number;
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
milliseconds: number;
|
|
47
|
+
meta?: Record<string, unknown>;
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
export interface FormatConfig {
|
|
41
51
|
rows: GanttRow[];
|
|
52
|
+
sidebarWidth: number;
|
|
42
53
|
minWidth: number;
|
|
43
54
|
rowHeight: number;
|
|
44
|
-
rowPadding: number;
|
|
45
55
|
includeToday: boolean;
|
|
46
56
|
}
|
|
47
57
|
|