Skip to content

Commit fabdc9f

Browse files
authored
Merge pull request #68 from dpschen/feature/support-js-dates-everywhere
feat: support date objects everywhere
2 parents c958f4b + 053f0fd commit fabdc9f

File tree

12 files changed

+98
-73
lines changed

12 files changed

+98
-73
lines changed

docs/GGanttChart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ The main component of Vue Ganttastic. Represents an entire chart and is meant to
88
| `precision` | string? | `"hour"` | Display precision of the time-axis. Possible values: `hour`, `day` and `month`. |
99
| `bar-start` | string | | Name of the property in bar objects that represents the start date.
1010
| `bar-end` | string | | Name of the property in bar objects that represents the end date .
11-
| `date-format` | string? | `"YYYY-MM-DD HH:mm"` | Datetime string format of `chart-start`, `chart-end` and the values of the `bar-start`, `bar-end` properties in bar objects. See [Day.js format tokens](https://day.js.org/docs/en/parse/string-format).
11+
| `date-format` | string \| false | `"YYYY-MM-DD HH:mm"` | Datetime string format of `chart-start`, `chart-end` and the values of the `bar-start`, `bar-end` properties in bar objects. See [Day.js format tokens](https://day.js.org/docs/en/parse/string-format). If the aforementioned properties are native JavaScript [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) objects in your use case, pass `false`.
1212
| `width` | string? | `"100%"` | Width of the chart (e.g. `80%` or `800px`)
1313
| `hide-timeaxis` | boolean? | `false` | Toggle visibility of the time axis.
1414
| `color-scheme` | string \| ColorScheme | `"default"` | Color scheme (theme) of the chart. Either use the name of one of the predefined schemes or pass a color-scheme-object of your own. See [color schemes](#color-schemes).

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/GGanttBar.vue

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
<div
33
:id="barConfig.id"
44
class="g-gantt-bar"
5-
:style="barStyle"
5+
:style="{
6+
...barConfig.style,
7+
position: 'absolute',
8+
top: `${rowHeight * 0.1}px`,
9+
left: `${xStart}px`,
10+
width: `${xEnd - xStart}px`,
11+
height: `${rowHeight * 0.8}px`,
12+
zIndex: isDragging ? 3 : 2
13+
}"
614
@mousedown="onMouseEvent"
715
@click="onMouseEvent"
816
@dblclick="onMouseEvent"
@@ -25,7 +33,7 @@
2533
</template>
2634

2735
<script setup lang="ts">
28-
import { computed, ref, toRefs, watch, type CSSProperties, onMounted, inject } from "vue"
36+
import { computed, ref, toRefs, watch, onMounted, inject } from "vue"
2937
3038
import useBarDragManagement from "../composables/useBarDragManagement.js"
3139
import useTimePositionMapping from "../composables/useTimePositionMapping.js"
@@ -84,10 +92,10 @@ const onMouseEvent = (e: MouseEvent) => {
8492
prepareForDrag()
8593
}
8694
const barContainer = barContainerEl?.value?.getBoundingClientRect()
87-
let datetime
88-
if (barContainer) {
89-
datetime = mapPositionToTime(e.clientX - barContainer.left)
95+
if (!barContainer) {
96+
return
9097
}
98+
const datetime = mapPositionToTime(e.clientX - barContainer.left)
9199
emitBarEvent(e, bar.value, datetime)
92100
}
93101
@@ -106,18 +114,6 @@ onMounted(() => {
106114
{ deep: true, immediate: true }
107115
)
108116
})
109-
110-
const barStyle = computed(() => {
111-
return {
112-
...barConfig.value.style,
113-
position: "absolute",
114-
top: `${rowHeight.value * 0.1}px`,
115-
left: `${xStart.value}px`,
116-
width: `${xEnd.value - xStart.value}px`,
117-
height: `${rowHeight.value * 0.8}px`,
118-
zIndex: isDragging.value ? 3 : 2
119-
} as CSSProperties
120-
})
121117
</script>
122118

123119
<style>

src/components/GGanttBarTooltip.vue

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
}"
1212
>
1313
<div class="g-gantt-tooltip-color-dot" :style="{ background: dotColor }" />
14-
<slot>
14+
<slot :bar="bar" :bar-start="barStartRaw" :bar-end="barEndRaw">
1515
{{ tooltipContent }}
1616
</slot>
1717
</div>
@@ -35,12 +35,12 @@ const TOOLTIP_FORMATS = {
3535
const DEFAULT_DOT_COLOR = "cadetblue"
3636
3737
const props = defineProps<{
38-
bar?: GanttBarObject
38+
bar: GanttBarObject | undefined
3939
modelValue: boolean
4040
}>()
4141
4242
const { bar } = toRefs(props)
43-
const config = provideConfig()
43+
const { precision, font, barStart, barEnd, rowHeight } = provideConfig()
4444
4545
const tooltipTop = ref("0px")
4646
const tooltipLeft = ref("0px")
@@ -59,7 +59,6 @@ watch(
5959
top: 0,
6060
left: 0
6161
}
62-
const { rowHeight } = config
6362
const leftValue = Math.max(left, 10)
6463
tooltipTop.value = `${top + rowHeight.value - 10}px`
6564
tooltipLeft.value = `${leftValue}px`
@@ -70,14 +69,17 @@ watch(
7069
const dotColor = computed(() => bar?.value?.ganttBarConfig.style?.background || DEFAULT_DOT_COLOR)
7170
7271
const { toDayjs } = useDayjsHelper()
73-
const { precision, font } = config
72+
73+
const barStartRaw = computed(() => bar.value?.[barStart.value])
74+
const barEndRaw = computed(() => bar.value?.[barEnd.value])
75+
7476
const tooltipContent = computed(() => {
75-
const format = TOOLTIP_FORMATS[precision.value]
7677
if (!bar?.value) {
7778
return ""
7879
}
79-
const barStartFormatted = toDayjs(bar.value, "start").format(format)
80-
const barEndFormatted = toDayjs(bar.value, "end").format(format)
80+
const format = TOOLTIP_FORMATS[precision.value]
81+
const barStartFormatted = toDayjs(barStartRaw.value).format(format)
82+
const barEndFormatted = toDayjs(barEndRaw.value).format(format)
8183
return `${barStartFormatted} \u2013 ${barEndFormatted}`
8284
})
8385
</script>

src/components/GGanttChart.vue

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,16 @@ import { colorSchemes, type ColorScheme } from "../color-schemes.js"
4949
import type { ColorSchemeKey } from "../color-schemes.js"
5050
import { CHART_ROWS_KEY, CONFIG_KEY, EMIT_BAR_EVENT_KEY } from "../provider/symbols.js"
5151
import type { GanttBarObject } from "../types"
52+
import { DEFAULT_DATE_FORMAT } from "../composables/useDayjsHelper"
5253
import { useElementSize } from "@vueuse/core"
5354
5455
export interface GGanttChartProps {
55-
chartStart: string
56-
chartEnd: string
56+
chartStart: string | Date
57+
chartEnd: string | Date
5758
precision?: "hour" | "day" | "month"
5859
barStart: string
5960
barEnd: string
60-
dateFormat?: string
61+
dateFormat?: string | false
6162
width?: string
6263
hideTimeaxis?: boolean
6364
colorScheme?: ColorSchemeKey | ColorScheme
@@ -78,7 +79,7 @@ export type GGanttChartConfig = ToRefs<Required<GGanttChartProps>> & {
7879
}
7980
8081
const props = withDefaults(defineProps<GGanttChartProps>(), {
81-
dateFormat: "YYYY-MM-DD HH:mm",
82+
dateFormat: DEFAULT_DATE_FORMAT,
8283
precision: "day",
8384
width: "100%",
8485
hideTimeaxis: false,
@@ -92,10 +93,13 @@ const props = withDefaults(defineProps<GGanttChartProps>(), {
9293
})
9394
9495
const emit = defineEmits<{
95-
(e: "click-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string }): void
96-
(e: "mousedown-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string }): void
97-
(e: "mouseup-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string }): void
98-
(e: "dblclick-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string }): void
96+
(e: "click-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string | Date }): void
97+
(
98+
e: "mousedown-bar",
99+
value: { bar: GanttBarObject; e: MouseEvent; datetime?: string | Date }
100+
): void
101+
(e: "mouseup-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string | Date }): void
102+
(e: "dblclick-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string | Date }): void
99103
(e: "mouseenter-bar", value: { bar: GanttBarObject; e: MouseEvent }): void
100104
(e: "mouseleave-bar", value: { bar: GanttBarObject; e: MouseEvent }): void
101105
(e: "dragstart-bar", value: { bar: GanttBarObject; e: MouseEvent }): void
@@ -108,7 +112,10 @@ const emit = defineEmits<{
108112
movedBars?: Map<GanttBarObject, { oldStart: string; oldEnd: string }>
109113
}
110114
): void
111-
(e: "contextmenu-bar", value: { bar: GanttBarObject; e: MouseEvent; datetime?: string }): void
115+
(
116+
e: "contextmenu-bar",
117+
value: { bar: GanttBarObject; e: MouseEvent; datetime?: string | Date }
118+
): void
112119
}>()
113120
114121
const { width, font, colorScheme } = toRefs(props)
@@ -168,7 +175,7 @@ const clearTooltip = () => {
168175
const emitBarEvent = (
169176
e: MouseEvent,
170177
bar: GanttBarObject,
171-
datetime?: string,
178+
datetime?: string | Date,
172179
movedBars?: Map<GanttBarObject, { oldStart: string; oldEnd: string }>
173180
) => {
174181
switch (e.type) {

src/components/GGanttRow.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const props = defineProps<{
3939
}>()
4040
4141
const emit = defineEmits<{
42-
(e: "drop", value: { e: MouseEvent; datetime: string }): void
42+
(e: "drop", value: { e: MouseEvent; datetime: string | Date }): void
4343
}>()
4444
4545
const { rowHeight, colors } = provideConfig()

src/composables/createBarDrag.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ref } from "vue"
1+
import { ref } from "vue"
22
import type { GGanttChartConfig } from "../components/GGanttChart.vue"
33
import provideConfig from "../provider/provideConfig.js"
44

src/composables/useBarDragManagement.ts

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default function useBarDragManagement() {
1515

1616
const movedBarsInDrag = new Map<GanttBarObject, { oldStart: string; oldEnd: string }>()
1717

18-
const { toDayjs } = useDayjsHelper()
18+
const { toDayjs, format } = useDayjsHelper()
1919

2020
const initDragOfBar = (bar: GanttBarObject, e: MouseEvent) => {
2121
const { initDrag } = createBarDrag(bar, onDrag, onEndDrag, config)
@@ -63,17 +63,19 @@ export default function useBarDragManagement() {
6363
switch (overlapType) {
6464
case "left":
6565
minuteDiff = overlapBarEnd.diff(currentBarStart, "minutes", true)
66-
overlapBar[barEnd.value] = currentBarStart.format(dateFormat.value)
67-
overlapBar[barStart.value] = overlapBarStart
68-
.subtract(minuteDiff, "minutes")
69-
.format(dateFormat.value)
66+
overlapBar[barEnd.value] = format(currentBar[barStart.value], dateFormat.value)
67+
overlapBar[barStart.value] = format(
68+
overlapBarStart.subtract(minuteDiff, "minutes"),
69+
dateFormat.value
70+
)
7071
break
7172
case "right":
7273
minuteDiff = currentBarEnd.diff(overlapBarStart, "minutes", true)
73-
overlapBar[barStart.value] = currentBarEnd.format(dateFormat.value)
74-
overlapBar[barEnd.value] = overlapBarEnd
75-
.add(minuteDiff, "minutes")
76-
.format(dateFormat.value)
74+
overlapBar[barStart.value] = format(currentBarEnd, dateFormat.value)
75+
overlapBar[barEnd.value] = format(
76+
overlapBarEnd.add(minuteDiff, "minutes"),
77+
dateFormat.value
78+
)
7779
break
7880
default:
7981
console.warn(
@@ -139,16 +141,21 @@ export default function useBarDragManagement() {
139141
const moveBarByMinutes = (bar: GanttBarObject, minutes: number, direction: "left" | "right") => {
140142
switch (direction) {
141143
case "left":
142-
bar[barStart.value] = toDayjs(bar, "start")
143-
.subtract(minutes, "minutes")
144-
.format(dateFormat.value)
145-
bar[barEnd.value] = toDayjs(bar, "end")
146-
.subtract(minutes, "minutes")
147-
.format(dateFormat.value)
144+
bar[barStart.value] = format(
145+
toDayjs(bar, "start").subtract(minutes, "minutes"),
146+
dateFormat.value
147+
)
148+
bar[barEnd.value] = format(
149+
toDayjs(bar, "end").subtract(minutes, "minutes"),
150+
dateFormat.value
151+
)
148152
break
149153
case "right":
150-
bar[barStart.value] = toDayjs(bar, "start").add(minutes, "minutes").format(dateFormat.value)
151-
bar[barEnd.value] = toDayjs(bar, "end").add(minutes, "minutes").format(dateFormat.value)
154+
bar[barStart.value] = format(
155+
toDayjs(bar, "start").add(minutes, "minutes"),
156+
dateFormat.value
157+
)
158+
bar[barEnd.value] = format(toDayjs(bar, "end").add(minutes, "minutes"), dateFormat.value)
152159
}
153160
fixOverlaps(bar)
154161
}

src/composables/useDayjsHelper.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
1-
import dayjs from "dayjs"
1+
import dayjs, { type Dayjs } from "dayjs"
22
import { computed } from "vue"
33

44
import type { GGanttChartConfig } from "../components/GGanttChart.vue"
55
import type { GanttBarObject } from "../types"
66
import provideConfig from "../provider/provideConfig.js"
77

8+
export const DEFAULT_DATE_FORMAT = "YYYY-MM-DD HH:mm"
9+
810
export default function useDayjsHelper(config: GGanttChartConfig = provideConfig()) {
911
const { chartStart, chartEnd, barStart, barEnd, dateFormat } = config
1012

1113
const chartStartDayjs = computed(() => toDayjs(chartStart.value))
1214
const chartEndDayjs = computed(() => toDayjs(chartEnd.value))
1315

14-
const toDayjs = (value: string | GanttBarObject, startOrEnd?: "start" | "end") => {
15-
if (typeof value === "string") {
16-
return dayjs(value, dateFormat.value, true)
16+
const toDayjs = (input: string | Date | GanttBarObject, startOrEnd?: "start" | "end") => {
17+
let value
18+
if (startOrEnd !== undefined && typeof input !== "string" && !(input instanceof Date)) {
19+
value = startOrEnd === "start" ? input[barStart.value] : input[barEnd.value]
1720
}
18-
if (startOrEnd == null) {
19-
throw Error(
20-
"VueGanttastic - toDayjs: passed a GanttBarObject as value, but did not provide start|end parameter."
21-
)
21+
if (typeof input === "string") {
22+
value = input
23+
} else if (input instanceof Date) {
24+
return dayjs(input)
2225
}
23-
const property = startOrEnd === "start" ? value[barStart.value] : value[barEnd.value]
24-
return dayjs(property, dateFormat.value, true)
26+
const format = dateFormat.value || DEFAULT_DATE_FORMAT
27+
return dayjs(value, format, true)
28+
}
29+
30+
const format = (input: string | Date | Dayjs, pattern?: string | false) => {
31+
if (pattern === false) {
32+
return input instanceof Date ? input : dayjs(input).toDate()
33+
}
34+
const inputDayjs = typeof input === "string" || input instanceof Date ? toDayjs(input) : input
35+
36+
return inputDayjs.format(pattern)
2537
}
2638

2739
return {
2840
chartStartDayjs,
2941
chartEndDayjs,
30-
toDayjs
42+
toDayjs,
43+
format
3144
}
3245
}

src/composables/useTimePositionMapping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import provideConfig from "../provider/provideConfig.js"
66

77
export default function useTimePositionMapping(config: GGanttChartConfig = provideConfig()) {
88
const { dateFormat, chartSize } = config
9-
const { chartStartDayjs, chartEndDayjs, toDayjs } = useDayjsHelper(config)
9+
const { chartStartDayjs, chartEndDayjs, toDayjs, format } = useDayjsHelper(config)
1010

1111
const totalNumOfMinutes = computed(() => {
1212
return chartEndDayjs.value.diff(chartStartDayjs.value, "minutes")
@@ -21,7 +21,7 @@ export default function useTimePositionMapping(config: GGanttChartConfig = provi
2121
const mapPositionToTime = (xPos: number) => {
2222
const width = chartSize.width.value || 0
2323
const diffFromStart = (xPos / width) * totalNumOfMinutes.value
24-
return chartStartDayjs.value.add(diffFromStart, "minutes").format(dateFormat.value)
24+
return format(chartStartDayjs.value.add(diffFromStart, "minutes"), dateFormat.value)
2525
}
2626

2727
return {

0 commit comments

Comments
 (0)