sprintify-ui 0.11.16 → 0.11.18
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/sprintify-ui.es.js +4245 -4193
- package/dist/types/components/BaseGantt.vue.d.ts +6 -1
- package/dist/types/components/BaseTooltip.vue.d.ts +5 -5
- package/dist/types/services/gantt/types.d.ts +4 -0
- package/package.json +1 -1
- package/src/components/BaseGantt.stories.js +75 -1
- package/src/components/BaseGantt.vue +94 -2
- package/src/components/BaseTooltip.stories.js +1 -1
- package/src/components/BaseTooltip.vue +35 -45
- package/src/services/gantt/types.ts +5 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GanttRow, GanttRowFormatted, GanttItemFormatted } from '@/services/gantt/types';
|
|
1
|
+
import { GanttRow, GanttRowFormatted, GanttItemFormatted, GanttRelationship } from '@/services/gantt/types';
|
|
2
2
|
import { VNode } from 'vue';
|
|
3
3
|
interface Props {
|
|
4
4
|
/**
|
|
@@ -25,6 +25,10 @@ interface Props {
|
|
|
25
25
|
* Flatten the Gantt chart by removing the hierarchy of rows and displaying all items in a single list. This is useful when there's only one level of rows and you want to maximize the vertical space.
|
|
26
26
|
*/
|
|
27
27
|
flatten?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Relationships between items, drawn as arrows from one item to another.
|
|
30
|
+
*/
|
|
31
|
+
relationships?: GanttRelationship[];
|
|
28
32
|
}
|
|
29
33
|
type __VLS_Slots = {
|
|
30
34
|
sidebarRow: (props: {
|
|
@@ -51,6 +55,7 @@ declare const __VLS_component: import("vue").DefineComponent<Props, {}, {}, {},
|
|
|
51
55
|
sidebarWidth: number;
|
|
52
56
|
rowHeight: number;
|
|
53
57
|
includeToday: boolean;
|
|
58
|
+
relationships: GanttRelationship[];
|
|
54
59
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
55
60
|
declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
|
|
56
61
|
export default _default;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { UseFloatingOptions } from '@floating-ui/vue';
|
|
2
2
|
type __VLS_Props = {
|
|
3
|
+
as?: string;
|
|
3
4
|
visible?: boolean;
|
|
4
5
|
text?: string | null | undefined;
|
|
5
|
-
class?: string[] | string | null | undefined;
|
|
6
6
|
floatingOptions?: UseFloatingOptions;
|
|
7
7
|
/**
|
|
8
8
|
* Whether the tooltip content is interactive or not.
|
|
@@ -13,16 +13,16 @@ type __VLS_Props = {
|
|
|
13
13
|
dark?: boolean;
|
|
14
14
|
offset?: number;
|
|
15
15
|
};
|
|
16
|
-
declare var
|
|
16
|
+
declare var __VLS_7: {}, __VLS_17: {};
|
|
17
17
|
type __VLS_Slots = {} & {
|
|
18
|
-
default?: (props: typeof
|
|
18
|
+
default?: (props: typeof __VLS_7) => any;
|
|
19
19
|
} & {
|
|
20
|
-
tooltip?: (props: typeof
|
|
20
|
+
tooltip?: (props: typeof __VLS_17) => any;
|
|
21
21
|
};
|
|
22
22
|
declare const __VLS_component: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
23
23
|
dark: boolean;
|
|
24
|
-
class: string | string[] | null;
|
|
25
24
|
text: string | null;
|
|
25
|
+
as: string;
|
|
26
26
|
visible: boolean;
|
|
27
27
|
floatingOptions: UseFloatingOptions;
|
|
28
28
|
interactive: boolean;
|
package/package.json
CHANGED
|
@@ -157,4 +157,78 @@ const SlotSidebarSlotsTemplate = (args) => ({
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
export const SlotSidebarSlots = SlotSidebarSlotsTemplate.bind({});
|
|
160
|
-
SlotSidebarSlots.args = {};
|
|
160
|
+
SlotSidebarSlots.args = {};
|
|
161
|
+
|
|
162
|
+
// Relationships story with unique item IDs
|
|
163
|
+
|
|
164
|
+
const relationshipRows = [];
|
|
165
|
+
|
|
166
|
+
let itemId = 1;
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < 5; i++) {
|
|
169
|
+
const items = [];
|
|
170
|
+
|
|
171
|
+
for (let j = 0; j < 2; j++) {
|
|
172
|
+
const start = DateTime.now()
|
|
173
|
+
.minus({ days: 30 })
|
|
174
|
+
.plus({ days: (i * 15) + (j * 20) });
|
|
175
|
+
|
|
176
|
+
const end = start.plus({ days: 15 });
|
|
177
|
+
|
|
178
|
+
const colors = ["blue", "green", "red", "indigo", "purple"];
|
|
179
|
+
const color = defaultColors[colors[i % colors.length]][500];
|
|
180
|
+
|
|
181
|
+
items.push({
|
|
182
|
+
id: itemId++,
|
|
183
|
+
name: `Task ${items.length + 1} of Project ${i + 1}`,
|
|
184
|
+
start: start.toISO(),
|
|
185
|
+
end: end.toISO(),
|
|
186
|
+
color: color,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
relationshipRows.push({
|
|
191
|
+
id: i + 1,
|
|
192
|
+
name: `Project ${i + 1}`,
|
|
193
|
+
items: items,
|
|
194
|
+
height: 30,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const relationships = [
|
|
199
|
+
{ fromId: 1, toId: 3 },
|
|
200
|
+
{ fromId: 3, toId: 5 },
|
|
201
|
+
{ fromId: 2, toId: 6 },
|
|
202
|
+
{ fromId: 5, toId: 9 },
|
|
203
|
+
];
|
|
204
|
+
|
|
205
|
+
const RelationshipsTemplate = (args) => ({
|
|
206
|
+
components: {
|
|
207
|
+
BaseCard,
|
|
208
|
+
BaseGantt,
|
|
209
|
+
},
|
|
210
|
+
setup() {
|
|
211
|
+
function onItemClick(item) {
|
|
212
|
+
alert(`Item "${item.name}" clicked`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function onRowClick(item) {
|
|
216
|
+
alert(`Row "${item.name}" clicked`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { args, onItemClick, onRowClick };
|
|
220
|
+
},
|
|
221
|
+
template: `
|
|
222
|
+
<BaseCard clipped>
|
|
223
|
+
<BaseGantt v-bind="args" @item:click="onItemClick" @row:click="onRowClick">
|
|
224
|
+
</BaseGantt>
|
|
225
|
+
</BaseCard>
|
|
226
|
+
`,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export const Relationships = RelationshipsTemplate.bind({});
|
|
230
|
+
Relationships.args = {
|
|
231
|
+
rows: relationshipRows,
|
|
232
|
+
relationships,
|
|
233
|
+
maxHeight: 500,
|
|
234
|
+
};
|
|
@@ -276,6 +276,45 @@
|
|
|
276
276
|
</div>
|
|
277
277
|
</div>
|
|
278
278
|
</li>
|
|
279
|
+
|
|
280
|
+
<!-- Relationship arrows -->
|
|
281
|
+
|
|
282
|
+
<svg
|
|
283
|
+
v-if="relationshipPaths.length"
|
|
284
|
+
class="absolute top-0 left-0 pointer-events-none"
|
|
285
|
+
:viewBox="`0 0 ${width} ${height}`"
|
|
286
|
+
:width="width"
|
|
287
|
+
:height="height"
|
|
288
|
+
>
|
|
289
|
+
<defs>
|
|
290
|
+
<marker
|
|
291
|
+
id="gantt-arrowhead"
|
|
292
|
+
markerWidth="8"
|
|
293
|
+
markerHeight="6"
|
|
294
|
+
refX="8"
|
|
295
|
+
refY="3"
|
|
296
|
+
orient="auto"
|
|
297
|
+
>
|
|
298
|
+
<polygon
|
|
299
|
+
points="0 0, 8 3, 0 6"
|
|
300
|
+
fill="black"
|
|
301
|
+
></polygon>
|
|
302
|
+
</marker>
|
|
303
|
+
</defs>
|
|
304
|
+
<g
|
|
305
|
+
v-for="rel in relationshipPaths"
|
|
306
|
+
:key="rel.id"
|
|
307
|
+
opacity="0.3"
|
|
308
|
+
>
|
|
309
|
+
<path
|
|
310
|
+
:d="rel.path"
|
|
311
|
+
fill="none"
|
|
312
|
+
stroke="black"
|
|
313
|
+
stroke-width="1.5"
|
|
314
|
+
marker-end="url(#gantt-arrowhead)"
|
|
315
|
+
></path>
|
|
316
|
+
</g>
|
|
317
|
+
</svg>
|
|
279
318
|
</ul>
|
|
280
319
|
|
|
281
320
|
<!-- Vertical lines -->
|
|
@@ -346,7 +385,7 @@
|
|
|
346
385
|
y1="0"
|
|
347
386
|
:y2="height"
|
|
348
387
|
stroke="red"
|
|
349
|
-
stroke-width="
|
|
388
|
+
stroke-width="1.5"
|
|
350
389
|
style="filter: drop-shadow(0px 0px 2px rgb(0 0 0 / 0.1));"
|
|
351
390
|
></line>
|
|
352
391
|
</svg>
|
|
@@ -358,7 +397,7 @@
|
|
|
358
397
|
<script lang="ts" setup>
|
|
359
398
|
import { useElementSize, useScroll } from '@vueuse/core';
|
|
360
399
|
import { Format } from '@/services/gantt/format';
|
|
361
|
-
import { FormatConfig, GanttRow, GanttRowFormatted, Group, Tick, NowLine, GanttItemFormatted } from '@/services/gantt/types';
|
|
400
|
+
import { FormatConfig, GanttRow, GanttRowFormatted, Group, Tick, NowLine, GanttItemFormatted, GanttRelationship } from '@/services/gantt/types';
|
|
362
401
|
import { cloneDeep, debounce } from 'lodash';
|
|
363
402
|
import { slate } from 'tailwindcss/colors';
|
|
364
403
|
import { VNode } from 'vue';
|
|
@@ -388,6 +427,10 @@ interface Props {
|
|
|
388
427
|
* Flatten the Gantt chart by removing the hierarchy of rows and displaying all items in a single list. This is useful when there's only one level of rows and you want to maximize the vertical space.
|
|
389
428
|
*/
|
|
390
429
|
flatten?: boolean;
|
|
430
|
+
/**
|
|
431
|
+
* Relationships between items, drawn as arrows from one item to another.
|
|
432
|
+
*/
|
|
433
|
+
relationships?: GanttRelationship[];
|
|
391
434
|
}
|
|
392
435
|
|
|
393
436
|
const props = withDefaults(defineProps<Props>(), {
|
|
@@ -395,6 +438,7 @@ const props = withDefaults(defineProps<Props>(), {
|
|
|
395
438
|
rowHeight: 40,
|
|
396
439
|
maxHeight: undefined,
|
|
397
440
|
includeToday: true,
|
|
441
|
+
relationships: () => [],
|
|
398
442
|
});
|
|
399
443
|
|
|
400
444
|
defineEmits<{
|
|
@@ -521,4 +565,52 @@ function isRowExpanded(rowId: string | number): boolean {
|
|
|
521
565
|
return expandedRows.value.has(rowId);
|
|
522
566
|
}
|
|
523
567
|
|
|
568
|
+
// Relationship arrows
|
|
569
|
+
|
|
570
|
+
const relationshipPaths = computed(() => {
|
|
571
|
+
if (!props.relationships.length || !rowsInternal.value.length) return [];
|
|
572
|
+
|
|
573
|
+
const positions = new Map<string | number, { x: number; centerY: number; width: number }>();
|
|
574
|
+
let cumulativeY = 0;
|
|
575
|
+
|
|
576
|
+
for (const row of rowsInternal.value) {
|
|
577
|
+
if (!props.flatten) {
|
|
578
|
+
cumulativeY += row.height;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
if (isRowExpanded(row.id)) {
|
|
582
|
+
for (const item of row.items) {
|
|
583
|
+
positions.set(item.id, {
|
|
584
|
+
x: item.x,
|
|
585
|
+
centerY: cumulativeY + item.y + item.barHeight / 2,
|
|
586
|
+
width: item.width,
|
|
587
|
+
});
|
|
588
|
+
cumulativeY += item.height;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return props.relationships
|
|
594
|
+
.map((rel) => {
|
|
595
|
+
const from = positions.get(rel.fromId);
|
|
596
|
+
const to = positions.get(rel.toId);
|
|
597
|
+
if (!from || !to) return null;
|
|
598
|
+
|
|
599
|
+
const startX = from.x + from.width;
|
|
600
|
+
const startY = from.centerY;
|
|
601
|
+
const endX = to.x;
|
|
602
|
+
const endY = to.centerY;
|
|
603
|
+
|
|
604
|
+
const TAIL_LENGTH = 20;
|
|
605
|
+
const curveEndX = endX - TAIL_LENGTH;
|
|
606
|
+
const dx = Math.abs(curveEndX - startX);
|
|
607
|
+
const offset = Math.max(20, dx * 0.3);
|
|
608
|
+
|
|
609
|
+
const path = `M ${startX} ${startY} C ${startX + offset} ${startY}, ${curveEndX - offset} ${endY}, ${curveEndX} ${endY} L ${endX} ${endY}`;
|
|
610
|
+
|
|
611
|
+
return { path, id: `${rel.fromId}-${rel.toId}` };
|
|
612
|
+
})
|
|
613
|
+
.filter((r): r is { path: string; id: string } => r !== null);
|
|
614
|
+
});
|
|
615
|
+
|
|
524
616
|
</script>
|
|
@@ -22,7 +22,7 @@ const Template = (args) => ({
|
|
|
22
22
|
template: `
|
|
23
23
|
<BaseCard>
|
|
24
24
|
<BaseCardRow size=sm>
|
|
25
|
-
<BaseTooltip class="inline-block" v-bind="args">
|
|
25
|
+
<BaseTooltip class="inline-block" title="test" v-bind="args">
|
|
26
26
|
<div>Hover me, the tooltip show appear outside the BaseCard</div>
|
|
27
27
|
</BaseTooltip>
|
|
28
28
|
</BaseCardRow>
|
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<component
|
|
3
|
+
:is="as"
|
|
3
4
|
ref="targetRef"
|
|
4
|
-
:class="classInternal"
|
|
5
5
|
>
|
|
6
6
|
<slot />
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
to="body"
|
|
11
|
-
>
|
|
12
|
-
<div
|
|
13
|
-
ref="tooltipRef"
|
|
14
|
-
class="fixed top-0 left-0 z-tooltip"
|
|
15
|
-
:class="[!interactive ? 'pointer-events-none' : '']"
|
|
16
|
-
:style="floatingStyles"
|
|
7
|
+
<Teleport
|
|
8
|
+
v-if="visible"
|
|
9
|
+
to="body"
|
|
17
10
|
>
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
leave-from-class="transform scale-100 opacity-100"
|
|
24
|
-
leave-to-class="transform scale-90 opacity-0"
|
|
11
|
+
<div
|
|
12
|
+
ref="tooltipRef"
|
|
13
|
+
class="fixed top-0 left-0 z-tooltip"
|
|
14
|
+
:class="[!interactive ? 'pointer-events-none' : '']"
|
|
15
|
+
:style="floatingStyles"
|
|
25
16
|
>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
<transition
|
|
18
|
+
enter-active-class="transition duration-200 ease-out"
|
|
19
|
+
enter-from-class="transform scale-90 opacity-0"
|
|
20
|
+
enter-to-class="transform scale-100 opacity-100"
|
|
21
|
+
leave-active-class="transition duration-75 ease-in"
|
|
22
|
+
leave-from-class="transform scale-100 opacity-100"
|
|
23
|
+
leave-to-class="transform scale-90 opacity-0"
|
|
29
24
|
>
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
<slot
|
|
26
|
+
v-if="showTooltip"
|
|
27
|
+
name="tooltip"
|
|
28
|
+
>
|
|
29
|
+
<div
|
|
30
|
+
class="text-xs max-w-xs leading-snug rounded-md pt-1.5 pb-2 px-3"
|
|
31
|
+
:class="[
|
|
32
|
+
dark ? 'bg-slate-900 text-white' : 'bg-white text-slate-900 ring-1 ring-black ring-opacity-10 shadow-md',
|
|
33
|
+
]"
|
|
34
|
+
v-html="text"
|
|
35
|
+
/>
|
|
36
|
+
</slot>
|
|
37
|
+
</transition>
|
|
38
|
+
</div>
|
|
39
|
+
</Teleport>
|
|
40
|
+
</component>
|
|
41
41
|
</template>
|
|
42
42
|
|
|
43
43
|
<script lang="ts" setup>
|
|
@@ -45,14 +45,10 @@ import { useTooltip } from '@/composables/tooltip';
|
|
|
45
45
|
import { unrefElement } from '@vueuse/core';
|
|
46
46
|
import { UseFloatingOptions } from '@floating-ui/vue';
|
|
47
47
|
|
|
48
|
-
defineOptions({
|
|
49
|
-
inheritAttrs: false,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
48
|
const props = withDefaults(defineProps<{
|
|
49
|
+
as?: string,
|
|
53
50
|
visible?: boolean,
|
|
54
51
|
text?: string | null | undefined;
|
|
55
|
-
class?: string[] | string | null | undefined;
|
|
56
52
|
floatingOptions?: UseFloatingOptions,
|
|
57
53
|
/**
|
|
58
54
|
* Whether the tooltip content is interactive or not.
|
|
@@ -63,9 +59,9 @@ const props = withDefaults(defineProps<{
|
|
|
63
59
|
dark?: boolean,
|
|
64
60
|
offset?: number,
|
|
65
61
|
}>(), {
|
|
62
|
+
as: 'div',
|
|
66
63
|
visible: true,
|
|
67
64
|
text: null,
|
|
68
|
-
class: null,
|
|
69
65
|
floatingOptions() {
|
|
70
66
|
return {
|
|
71
67
|
placement: 'top-start',
|
|
@@ -85,10 +81,4 @@ const tooltipRef = ref<HTMLElement | null>(null)
|
|
|
85
81
|
|
|
86
82
|
const { floatingStyles, showTooltip } = useTooltip(targetInternal, tooltipRef, props.interactive, props.delay, props.floatingOptions, props.offset);
|
|
87
83
|
|
|
88
|
-
const classInternal = computed(() => {
|
|
89
|
-
return [
|
|
90
|
-
props.class,
|
|
91
|
-
];
|
|
92
|
-
});
|
|
93
|
-
|
|
94
84
|
</script>
|