nuxt-ui-elements-pro 0.1.10 → 0.1.11
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/module.d.mts +5 -0
- package/dist/module.json +1 -1
- package/dist/module.mjs +240 -1
- package/dist/runtime/components/EventCalendar.vue +2 -2
- package/dist/runtime/components/FeedbackWidget.d.vue.ts +118 -0
- package/dist/runtime/components/FeedbackWidget.vue +141 -0
- package/dist/runtime/components/FeedbackWidget.vue.d.ts +118 -0
- package/dist/runtime/components/GanttChart.d.vue.ts +138 -0
- package/dist/runtime/components/GanttChart.vue +206 -0
- package/dist/runtime/components/GanttChart.vue.d.ts +138 -0
- package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue +23 -3
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.d.vue.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue +27 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetButton.vue.d.ts +22 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.d.vue.ts +42 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue +288 -0
- package/dist/runtime/components/feedback-widget/FeedbackWidgetPanel.vue.d.ts +42 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.d.vue.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue +70 -0
- package/dist/runtime/components/gantt-chart/GanttChartDependencies.vue.d.ts +16 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.d.vue.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue +56 -0
- package/dist/runtime/components/gantt-chart/GanttChartHeader.vue.d.ts +41 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.d.vue.ts +34 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue +193 -0
- package/dist/runtime/components/gantt-chart/GanttChartTimeline.vue.d.ts +34 -0
- package/dist/runtime/composables/useEventCalendar.d.ts +2 -0
- package/dist/runtime/composables/useEventCalendar.js +8 -6
- package/dist/runtime/composables/useEventCalendarContext.d.ts +2 -7
- package/dist/runtime/composables/useEventCalendarContext.js +4 -11
- package/dist/runtime/composables/useFeedbackWidget.d.ts +46 -0
- package/dist/runtime/composables/useFeedbackWidget.js +137 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.d.ts +3 -0
- package/dist/runtime/composables/useFeedbackWidgetContext.js +4 -0
- package/dist/runtime/composables/useGanttChart.d.ts +52 -0
- package/dist/runtime/composables/useGanttChart.js +224 -0
- package/dist/runtime/composables/useGanttChartContext.d.ts +3 -0
- package/dist/runtime/composables/useGanttChartContext.js +4 -0
- package/dist/runtime/composables/useGanttChartDragDrop.d.ts +28 -0
- package/dist/runtime/composables/useGanttChartDragDrop.js +68 -0
- package/dist/runtime/composables/useGanttChartKeyboard.d.ts +14 -0
- package/dist/runtime/composables/useGanttChartKeyboard.js +92 -0
- package/dist/runtime/composables/useGanttChartResize.d.ts +26 -0
- package/dist/runtime/composables/useGanttChartResize.js +89 -0
- package/dist/runtime/composables/useOrgChartContext.d.ts +2 -7
- package/dist/runtime/composables/useOrgChartContext.js +4 -11
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/server/api/_feedback.post.d.ts +3 -0
- package/dist/runtime/server/api/_feedback.post.js +115 -0
- package/dist/runtime/server/nodemailer.d.ts +29 -0
- package/dist/runtime/server/utils/feedback-captcha.d.ts +1 -0
- package/dist/runtime/server/utils/feedback-captcha.js +27 -0
- package/dist/runtime/server/utils/feedback-rate-limit.d.ts +4 -0
- package/dist/runtime/server/utils/feedback-rate-limit.js +26 -0
- package/dist/runtime/types/event-calendar.d.ts +10 -4
- package/dist/runtime/types/feedback-widget.d.ts +110 -0
- package/dist/runtime/types/feedback-widget.js +0 -0
- package/dist/runtime/types/gantt-chart.d.ts +223 -0
- package/dist/runtime/types/gantt-chart.js +0 -0
- package/dist/runtime/types/index.d.ts +4 -0
- package/dist/runtime/types/index.js +4 -0
- package/dist/runtime/utils/createComponentContext.d.ts +15 -0
- package/dist/runtime/utils/createComponentContext.js +17 -0
- package/dist/runtime/utils/date.d.ts +10 -0
- package/dist/runtime/utils/date.js +9 -0
- package/dist/runtime/utils/event-calendar.d.ts +1 -9
- package/dist/runtime/utils/event-calendar.js +2 -9
- package/dist/runtime/utils/gantt-chart.d.ts +85 -0
- package/dist/runtime/utils/gantt-chart.js +549 -0
- package/dist/runtime/utils/recurrence.js +2 -1
- package/package.json +17 -8
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useSlots } from "vue";
|
|
3
|
+
import { useGanttChartContext } from "../../composables/useGanttChartContext";
|
|
4
|
+
import GanttChartDependencies from "./GanttChartDependencies.vue";
|
|
5
|
+
const ctx = useGanttChartContext();
|
|
6
|
+
const slots = useSlots();
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div data-slot="timeline" :class="ctx.ui.value.timeline({ class: ctx.propUi.value?.timeline })" :style="{ width: `${ctx.layout.value.totalWidth}px` }">
|
|
11
|
+
<!-- Sticky header -->
|
|
12
|
+
<div data-slot="timelineHeader" :class="ctx.ui.value.timelineHeader({ class: ctx.propUi.value?.timelineHeader })">
|
|
13
|
+
<!-- Top row: header groups -->
|
|
14
|
+
<div data-slot="headerGroupRow" :class="ctx.ui.value.headerGroupRow({ class: ctx.propUi.value?.headerGroupRow })">
|
|
15
|
+
<div
|
|
16
|
+
v-for="(group, i) in ctx.layout.value.headerGroups"
|
|
17
|
+
:key="i"
|
|
18
|
+
data-slot="headerGroup"
|
|
19
|
+
:class="ctx.ui.value.headerGroup({ class: ctx.propUi.value?.headerGroup })"
|
|
20
|
+
:style="{ width: `${group.widthPx}px` }"
|
|
21
|
+
>
|
|
22
|
+
{{ group.label }}
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
<!-- Bottom row: columns -->
|
|
26
|
+
<div data-slot="headerColumnRow" :class="ctx.ui.value.headerColumnRow({ class: ctx.propUi.value?.headerColumnRow })">
|
|
27
|
+
<div
|
|
28
|
+
v-for="(col, i) in ctx.layout.value.columns"
|
|
29
|
+
:key="i"
|
|
30
|
+
data-slot="headerColumn"
|
|
31
|
+
:class="[
|
|
32
|
+
ctx.ui.value.headerColumn({ class: ctx.propUi.value?.headerColumn }),
|
|
33
|
+
col.isWeekend && ctx.ui.value.headerColumnWeekend({ class: ctx.propUi.value?.headerColumnWeekend }),
|
|
34
|
+
col.isToday && ctx.ui.value.headerColumnToday({ class: ctx.propUi.value?.headerColumnToday })
|
|
35
|
+
]"
|
|
36
|
+
:style="{ width: `${col.widthPx}px` }"
|
|
37
|
+
>
|
|
38
|
+
{{ col.label }}
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
<!-- Grid body -->
|
|
44
|
+
<div data-slot="grid" :class="ctx.ui.value.grid({ class: ctx.propUi.value?.grid })" :style="{ height: `${ctx.layout.value.totalHeight}px` }">
|
|
45
|
+
<!-- Vertical grid lines -->
|
|
46
|
+
<div
|
|
47
|
+
v-for="(col, i) in ctx.layout.value.columns"
|
|
48
|
+
:key="`line-${i}`"
|
|
49
|
+
:class="[
|
|
50
|
+
ctx.ui.value.gridLine({ class: ctx.propUi.value?.gridLine }),
|
|
51
|
+
col.isWeekend && ctx.ui.value.weekendColumn({ class: ctx.propUi.value?.weekendColumn })
|
|
52
|
+
]"
|
|
53
|
+
:style="{ left: `${col.leftPx}px`, width: `${col.widthPx}px`, height: '100%' }"
|
|
54
|
+
/>
|
|
55
|
+
|
|
56
|
+
<!-- Row stripes -->
|
|
57
|
+
<div
|
|
58
|
+
v-for="(bar, i) in ctx.layout.value.taskBars"
|
|
59
|
+
:key="`row-${bar.task.id}`"
|
|
60
|
+
:class="[
|
|
61
|
+
ctx.ui.value.row({ class: ctx.propUi.value?.row }),
|
|
62
|
+
i % 2 === 0 && ctx.ui.value.rowEven({ class: ctx.propUi.value?.rowEven })
|
|
63
|
+
]"
|
|
64
|
+
:style="{ top: `${i * ctx.rowHeight.value}px`, height: `${ctx.rowHeight.value}px` }"
|
|
65
|
+
/>
|
|
66
|
+
|
|
67
|
+
<!-- Today marker -->
|
|
68
|
+
<div
|
|
69
|
+
v-if="ctx.layout.value.todayOffsetPx !== null"
|
|
70
|
+
data-slot="todayMarker"
|
|
71
|
+
:class="ctx.ui.value.todayMarker({ class: ctx.propUi.value?.todayMarker })"
|
|
72
|
+
:style="{ left: `${ctx.layout.value.todayOffsetPx}px`, height: '100%' }"
|
|
73
|
+
>
|
|
74
|
+
<slot name="today-marker" />
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<!-- Dependency arrows (rendered before task bars so they appear behind them) -->
|
|
78
|
+
<GanttChartDependencies>
|
|
79
|
+
<template v-if="slots.dependency" #default="depProps">
|
|
80
|
+
<slot name="dependency" v-bind="depProps" />
|
|
81
|
+
</template>
|
|
82
|
+
</GanttChartDependencies>
|
|
83
|
+
|
|
84
|
+
<!-- Task bars -->
|
|
85
|
+
<template v-for="bar in ctx.layout.value.taskBars" :key="bar.task.id">
|
|
86
|
+
<!-- Milestone -->
|
|
87
|
+
<div
|
|
88
|
+
v-if="bar.isMilestone"
|
|
89
|
+
data-slot="milestone"
|
|
90
|
+
:class="ctx.ui.value.milestone({ class: ctx.propUi.value?.milestone })"
|
|
91
|
+
:style="{
|
|
92
|
+
left: `${bar.leftPx - 7}px`,
|
|
93
|
+
top: `${bar.rowIndex * ctx.rowHeight.value + (ctx.rowHeight.value - 14) / 2}px`,
|
|
94
|
+
paddingLeft: `${bar.depth * 16}px`
|
|
95
|
+
}"
|
|
96
|
+
:data-task-id="bar.task.id"
|
|
97
|
+
:data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
|
|
98
|
+
role="treeitem"
|
|
99
|
+
:aria-label="`Milestone: ${bar.task.title}`"
|
|
100
|
+
@click="ctx.handleTaskClick(bar.task, $event)"
|
|
101
|
+
>
|
|
102
|
+
<slot name="milestone" :task="bar.task.original" :bar="bar">
|
|
103
|
+
<div :class="ctx.ui.value.milestoneDiamond({ class: ctx.propUi.value?.milestoneDiamond })" :style="ctx.getTaskStyle(bar.task)" />
|
|
104
|
+
</slot>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Group bar -->
|
|
108
|
+
<div
|
|
109
|
+
v-else-if="bar.isGroup"
|
|
110
|
+
data-slot="groupBar"
|
|
111
|
+
:class="ctx.ui.value.groupBar({ class: ctx.propUi.value?.groupBar })"
|
|
112
|
+
:style="{
|
|
113
|
+
left: `${bar.leftPx}px`,
|
|
114
|
+
width: `${bar.widthPx}px`,
|
|
115
|
+
top: `${bar.rowIndex * ctx.rowHeight.value}px`,
|
|
116
|
+
height: `${ctx.rowHeight.value}px`,
|
|
117
|
+
paddingLeft: `${bar.depth * 16}px`
|
|
118
|
+
}"
|
|
119
|
+
:data-task-id="bar.task.id"
|
|
120
|
+
:data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
|
|
121
|
+
role="treeitem"
|
|
122
|
+
:aria-expanded="bar.isExpanded"
|
|
123
|
+
:aria-label="`${bar.task.title}, ${bar.childCount} subtasks`"
|
|
124
|
+
@click="ctx.handleTaskClick(bar.task, $event)"
|
|
125
|
+
>
|
|
126
|
+
<slot name="group-header" :task="bar.task.original" :bar="bar" :expanded="bar.isExpanded" :toggle="() => ctx.toggleExpand(bar.task.id)">
|
|
127
|
+
<button
|
|
128
|
+
:class="ctx.ui.value.groupToggle({ class: ctx.propUi.value?.groupToggle })"
|
|
129
|
+
:aria-label="bar.isExpanded ? 'Collapse group' : 'Expand group'"
|
|
130
|
+
@click.stop="ctx.toggleExpand(bar.task.id)"
|
|
131
|
+
>
|
|
132
|
+
<UIcon :name="bar.isExpanded ? 'i-lucide-chevron-down' : 'i-lucide-chevron-right'" class="size-3" />
|
|
133
|
+
</button>
|
|
134
|
+
<span :class="ctx.ui.value.groupTitle({ class: ctx.propUi.value?.groupTitle })">{{ bar.task.title }}</span>
|
|
135
|
+
<!-- Summary bar shape -->
|
|
136
|
+
<div :class="ctx.ui.value.groupBarShape({ class: ctx.propUi.value?.groupBarShape })" :style="ctx.getTaskStyle(bar.task)" />
|
|
137
|
+
<div :class="ctx.ui.value.groupBarEndCap({ class: ctx.propUi.value?.groupBarEndCap })" :style="{ left: '0', ...ctx.getTaskStyle(bar.task) }" />
|
|
138
|
+
<div :class="ctx.ui.value.groupBarEndCap({ class: ctx.propUi.value?.groupBarEndCap })" :style="{ right: '0', ...ctx.getTaskStyle(bar.task) }" />
|
|
139
|
+
</slot>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<!-- Regular task bar -->
|
|
143
|
+
<div
|
|
144
|
+
v-else
|
|
145
|
+
data-slot="taskBar"
|
|
146
|
+
:class="[
|
|
147
|
+
ctx.ui.value.taskBar({ class: ctx.propUi.value?.taskBar }),
|
|
148
|
+
ctx.draggedTaskId.value === bar.task.id && 'opacity-50'
|
|
149
|
+
]"
|
|
150
|
+
:style="{
|
|
151
|
+
left: `${ctx.draggedTaskId.value === bar.task.id && ctx.dragPreview.value ? ctx.dragPreview.value.leftPx : ctx.resizingTaskId.value === bar.task.id && ctx.resizePreview.value ? ctx.resizePreview.value.leftPx : bar.leftPx}px`,
|
|
152
|
+
width: `${ctx.resizingTaskId.value === bar.task.id && ctx.resizePreview.value ? ctx.resizePreview.value.widthPx : bar.widthPx}px`,
|
|
153
|
+
top: `${bar.rowIndex * ctx.rowHeight.value + 4}px`,
|
|
154
|
+
height: `${ctx.rowHeight.value - 8}px`,
|
|
155
|
+
marginLeft: `${bar.depth * 16}px`
|
|
156
|
+
}"
|
|
157
|
+
:data-task-id="bar.task.id"
|
|
158
|
+
:data-focused="ctx.focusedTaskId.value === bar.task.id || void 0"
|
|
159
|
+
role="treeitem"
|
|
160
|
+
:aria-label="bar.task.title"
|
|
161
|
+
@pointerdown="ctx.editable.value && bar.task.draggable ? ctx.onDragStart(bar, $event) : void 0"
|
|
162
|
+
@click="ctx.handleTaskClick(bar.task, $event)"
|
|
163
|
+
>
|
|
164
|
+
<slot name="task-bar" :task="bar.task.original" :bar="bar">
|
|
165
|
+
<!-- Background -->
|
|
166
|
+
<div :class="ctx.ui.value.taskBarInner({ class: ctx.propUi.value?.taskBarInner })" :style="ctx.getTaskStyle(bar.task)" />
|
|
167
|
+
<!-- Progress fill -->
|
|
168
|
+
<div
|
|
169
|
+
v-if="bar.task.progress > 0"
|
|
170
|
+
:class="ctx.ui.value.taskBarProgress({ class: ctx.propUi.value?.taskBarProgress })"
|
|
171
|
+
:style="{ width: `${bar.task.progress}%`, ...ctx.getTaskStyle(bar.task) }"
|
|
172
|
+
/>
|
|
173
|
+
<span :class="ctx.ui.value.taskBarTitle({ class: ctx.propUi.value?.taskBarTitle })">{{ bar.task.title }}</span>
|
|
174
|
+
</slot>
|
|
175
|
+
<!-- Left resize handle -->
|
|
176
|
+
<div
|
|
177
|
+
v-if="ctx.editable.value && bar.task.resizable"
|
|
178
|
+
:class="ctx.ui.value.resizeHandleLeft({ class: ctx.propUi.value?.resizeHandleLeft })"
|
|
179
|
+
:style="ctx.getTaskStyle(bar.task)"
|
|
180
|
+
@pointerdown.stop="ctx.onResizePointerDown(bar, 'start', $event)"
|
|
181
|
+
/>
|
|
182
|
+
<!-- Right resize handle -->
|
|
183
|
+
<div
|
|
184
|
+
v-if="ctx.editable.value && bar.task.resizable"
|
|
185
|
+
:class="ctx.ui.value.resizeHandleRight({ class: ctx.propUi.value?.resizeHandleRight })"
|
|
186
|
+
:style="ctx.getTaskStyle(bar.task)"
|
|
187
|
+
@pointerdown.stop="ctx.onResizePointerDown(bar, 'end', $event)"
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
</template>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</template>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
declare var __VLS_1: {}, __VLS_10: {
|
|
2
|
+
paths: import("../../types/gantt-chart.js").GanttDependencyPath[];
|
|
3
|
+
}, __VLS_12: {
|
|
4
|
+
task: import("../../types/gantt-chart.js").GanttTask;
|
|
5
|
+
bar: import("../../types/gantt-chart.js").GanttTaskBar;
|
|
6
|
+
}, __VLS_14: {
|
|
7
|
+
task: import("../../types/gantt-chart.js").GanttTask;
|
|
8
|
+
bar: import("../../types/gantt-chart.js").GanttTaskBar;
|
|
9
|
+
expanded: boolean;
|
|
10
|
+
toggle: () => void;
|
|
11
|
+
}, __VLS_21: {
|
|
12
|
+
task: import("../../types/gantt-chart.js").GanttTask;
|
|
13
|
+
bar: import("../../types/gantt-chart.js").GanttTaskBar;
|
|
14
|
+
};
|
|
15
|
+
type __VLS_Slots = {} & {
|
|
16
|
+
'today-marker'?: (props: typeof __VLS_1) => any;
|
|
17
|
+
} & {
|
|
18
|
+
dependency?: (props: typeof __VLS_10) => any;
|
|
19
|
+
} & {
|
|
20
|
+
milestone?: (props: typeof __VLS_12) => any;
|
|
21
|
+
} & {
|
|
22
|
+
'group-header'?: (props: typeof __VLS_14) => any;
|
|
23
|
+
} & {
|
|
24
|
+
'task-bar'?: (props: typeof __VLS_21) => any;
|
|
25
|
+
};
|
|
26
|
+
declare const __VLS_base: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
27
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
28
|
+
declare const _default: typeof __VLS_export;
|
|
29
|
+
export default _default;
|
|
30
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
31
|
+
new (): {
|
|
32
|
+
$slots: S;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
@@ -23,11 +23,13 @@ export declare function useEventCalendar(options: UseEventCalendarOptions): {
|
|
|
23
23
|
startHour: number;
|
|
24
24
|
endHour: number;
|
|
25
25
|
slotDuration: 15 | 30 | 60;
|
|
26
|
+
scrollTime: number;
|
|
26
27
|
}>;
|
|
27
28
|
dayConfig: import("vue").ComputedRef<{
|
|
28
29
|
startHour: number;
|
|
29
30
|
endHour: number;
|
|
30
31
|
slotDuration: 15 | 30 | 60;
|
|
32
|
+
scrollTime: number;
|
|
31
33
|
}>;
|
|
32
34
|
monthWeeks: import("vue").ComputedRef<CalendarWeek[]>;
|
|
33
35
|
monthWeekLayouts: import("vue").ComputedRef<MonthWeekLayout[]>;
|
|
@@ -23,14 +23,16 @@ export function useEventCalendar(options) {
|
|
|
23
23
|
fixedWeeks: options.monthOptions()?.fixedWeeks ?? true
|
|
24
24
|
}));
|
|
25
25
|
const weekConfig = computed(() => ({
|
|
26
|
-
startHour: options.weekOptions()?.startHour ??
|
|
27
|
-
endHour: options.weekOptions()?.endHour ??
|
|
28
|
-
slotDuration: options.weekOptions()?.slotDuration ?? 30
|
|
26
|
+
startHour: options.weekOptions()?.startHour ?? 0,
|
|
27
|
+
endHour: options.weekOptions()?.endHour ?? 24,
|
|
28
|
+
slotDuration: options.weekOptions()?.slotDuration ?? 30,
|
|
29
|
+
scrollTime: options.weekOptions()?.scrollTime ?? 7
|
|
29
30
|
}));
|
|
30
31
|
const dayConfig = computed(() => ({
|
|
31
|
-
startHour: options.dayOptions()?.startHour ??
|
|
32
|
-
endHour: options.dayOptions()?.endHour ??
|
|
33
|
-
slotDuration: options.dayOptions()?.slotDuration ?? 30
|
|
32
|
+
startHour: options.dayOptions()?.startHour ?? 0,
|
|
33
|
+
endHour: options.dayOptions()?.endHour ?? 24,
|
|
34
|
+
slotDuration: options.dayOptions()?.slotDuration ?? 30,
|
|
35
|
+
scrollTime: options.dayOptions()?.scrollTime ?? 7
|
|
34
36
|
}));
|
|
35
37
|
const displayDate = computed(() => {
|
|
36
38
|
const mv = options.modelValue();
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type { InjectionKey } from "vue";
|
|
2
1
|
import type { EventCalendarContext } from "../types/event-calendar.js";
|
|
3
|
-
export declare const eventCalendarContextKey: InjectionKey<EventCalendarContext>;
|
|
4
|
-
|
|
5
|
-
* Inject the EventCalendar context in a sub-component.
|
|
6
|
-
* Throws if called outside an <UEEventCalendar> root.
|
|
7
|
-
*/
|
|
8
|
-
export declare function useEventCalendarContext(): EventCalendarContext;
|
|
2
|
+
export declare const eventCalendarContextKey: import("vue").InjectionKey<EventCalendarContext>;
|
|
3
|
+
export declare const useEventCalendarContext: () => EventCalendarContext;
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
if (!ctx) {
|
|
6
|
-
throw new Error(
|
|
7
|
-
"[EventCalendar] useEventCalendarContext() was called outside of an <UEEventCalendar> root. Sub-components (UEEventCalendarHeader, UEEventCalendarMonthView, UEEventCalendarTimeGrid, UEEventCalendarListView) must be used as children of <UEEventCalendar>."
|
|
8
|
-
);
|
|
9
|
-
}
|
|
10
|
-
return ctx;
|
|
11
|
-
}
|
|
1
|
+
import { createComponentContext } from "../utils/createComponentContext.js";
|
|
2
|
+
const { key, useContext } = createComponentContext("EventCalendar");
|
|
3
|
+
export const eventCalendarContextKey = key;
|
|
4
|
+
export const useEventCalendarContext = useContext;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { FeedbackType, FeedbackTypeId, FeedbackSubmission, FeedbackSubmitResult, FeedbackWidgetPosition, CaptchaProvider } from "../types/feedback-widget.js";
|
|
2
|
+
export interface UseFeedbackWidgetOptions {
|
|
3
|
+
feedbackTypes: () => FeedbackType[];
|
|
4
|
+
position: () => FeedbackWidgetPosition;
|
|
5
|
+
showScreenshot: () => boolean;
|
|
6
|
+
emailPlaceholder: () => string;
|
|
7
|
+
messagePlaceholder: () => string;
|
|
8
|
+
captchaEnabled: () => boolean;
|
|
9
|
+
captchaProvider: () => CaptchaProvider | null;
|
|
10
|
+
captchaSiteKey: () => string | null;
|
|
11
|
+
submitEndpoint: () => string;
|
|
12
|
+
onSubmit: () => ((submission: FeedbackSubmission) => Promise<FeedbackSubmitResult> | FeedbackSubmitResult) | undefined;
|
|
13
|
+
onOpen: () => void;
|
|
14
|
+
onClose: () => void;
|
|
15
|
+
onSuccess: (submission: FeedbackSubmission) => void;
|
|
16
|
+
onError: (error: string) => void;
|
|
17
|
+
}
|
|
18
|
+
export declare function useFeedbackWidget(options: UseFeedbackWidgetOptions): {
|
|
19
|
+
isOpen: import("vue").Ref<boolean, boolean>;
|
|
20
|
+
isSubmitting: import("vue").Ref<boolean, boolean>;
|
|
21
|
+
isSubmitted: import("vue").Ref<boolean, boolean>;
|
|
22
|
+
submitError: import("vue").Ref<string | null, string | null>;
|
|
23
|
+
selectedType: import("vue").Ref<FeedbackTypeId | null, FeedbackTypeId | null>;
|
|
24
|
+
email: import("vue").Ref<string, string>;
|
|
25
|
+
message: import("vue").Ref<string, string>;
|
|
26
|
+
screenshot: import("vue").Ref<string | null, string | null>;
|
|
27
|
+
honeypot: import("vue").Ref<string, string>;
|
|
28
|
+
captchaToken: import("vue").Ref<string | null, string | null>;
|
|
29
|
+
feedbackTypes: import("vue").ComputedRef<FeedbackType[]>;
|
|
30
|
+
position: import("vue").ComputedRef<FeedbackWidgetPosition>;
|
|
31
|
+
showScreenshot: import("vue").ComputedRef<boolean>;
|
|
32
|
+
emailPlaceholder: import("vue").ComputedRef<string>;
|
|
33
|
+
messagePlaceholder: import("vue").ComputedRef<string>;
|
|
34
|
+
captchaEnabled: import("vue").ComputedRef<boolean>;
|
|
35
|
+
captchaProvider: import("vue").ComputedRef<CaptchaProvider | null>;
|
|
36
|
+
captchaSiteKey: import("vue").ComputedRef<string | null>;
|
|
37
|
+
isValid: import("vue").ComputedRef<boolean>;
|
|
38
|
+
open: () => void;
|
|
39
|
+
close: () => void;
|
|
40
|
+
toggle: () => void;
|
|
41
|
+
reset: () => void;
|
|
42
|
+
submit: () => Promise<void>;
|
|
43
|
+
captureScreenshot: () => Promise<void>;
|
|
44
|
+
removeScreenshot: () => void;
|
|
45
|
+
setCaptchaToken: (token: string) => void;
|
|
46
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { computed, ref } from "vue";
|
|
2
|
+
export function useFeedbackWidget(options) {
|
|
3
|
+
const isOpen = ref(false);
|
|
4
|
+
const isSubmitting = ref(false);
|
|
5
|
+
const isSubmitted = ref(false);
|
|
6
|
+
const submitError = ref(null);
|
|
7
|
+
const selectedType = ref(null);
|
|
8
|
+
const email = ref("");
|
|
9
|
+
const message = ref("");
|
|
10
|
+
const screenshot = ref(null);
|
|
11
|
+
const honeypot = ref("");
|
|
12
|
+
const captchaToken = ref(null);
|
|
13
|
+
const feedbackTypes = computed(() => options.feedbackTypes());
|
|
14
|
+
const position = computed(() => options.position());
|
|
15
|
+
const showScreenshot = computed(() => options.showScreenshot());
|
|
16
|
+
const emailPlaceholder = computed(() => options.emailPlaceholder());
|
|
17
|
+
const messagePlaceholder = computed(() => options.messagePlaceholder());
|
|
18
|
+
const captchaEnabled = computed(() => options.captchaEnabled());
|
|
19
|
+
const captchaProvider = computed(() => options.captchaProvider());
|
|
20
|
+
const captchaSiteKey = computed(() => options.captchaSiteKey());
|
|
21
|
+
const isValid = computed(() => {
|
|
22
|
+
return selectedType.value !== null && email.value.trim().length > 0 && message.value.trim().length > 0 && (!captchaEnabled.value || !!captchaToken.value);
|
|
23
|
+
});
|
|
24
|
+
function open() {
|
|
25
|
+
isOpen.value = true;
|
|
26
|
+
options.onOpen();
|
|
27
|
+
}
|
|
28
|
+
function close() {
|
|
29
|
+
isOpen.value = false;
|
|
30
|
+
options.onClose();
|
|
31
|
+
}
|
|
32
|
+
function toggle() {
|
|
33
|
+
if (isOpen.value) close();
|
|
34
|
+
else open();
|
|
35
|
+
}
|
|
36
|
+
function reset() {
|
|
37
|
+
selectedType.value = null;
|
|
38
|
+
email.value = "";
|
|
39
|
+
message.value = "";
|
|
40
|
+
screenshot.value = null;
|
|
41
|
+
honeypot.value = "";
|
|
42
|
+
captchaToken.value = null;
|
|
43
|
+
isSubmitted.value = false;
|
|
44
|
+
submitError.value = null;
|
|
45
|
+
}
|
|
46
|
+
async function captureScreenshot() {
|
|
47
|
+
try {
|
|
48
|
+
const { toPng } = await import("html-to-image");
|
|
49
|
+
const dataUrl = await toPng(document.body, {
|
|
50
|
+
quality: 0.8,
|
|
51
|
+
pixelRatio: 1
|
|
52
|
+
});
|
|
53
|
+
screenshot.value = dataUrl;
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function removeScreenshot() {
|
|
58
|
+
screenshot.value = null;
|
|
59
|
+
}
|
|
60
|
+
function setCaptchaToken(token) {
|
|
61
|
+
captchaToken.value = token;
|
|
62
|
+
}
|
|
63
|
+
async function submit() {
|
|
64
|
+
if (!isValid.value || isSubmitting.value) return;
|
|
65
|
+
isSubmitting.value = true;
|
|
66
|
+
submitError.value = null;
|
|
67
|
+
const submission = {
|
|
68
|
+
type: selectedType.value,
|
|
69
|
+
email: email.value.trim(),
|
|
70
|
+
message: message.value.trim(),
|
|
71
|
+
screenshot: screenshot.value ?? void 0,
|
|
72
|
+
_hp: honeypot.value,
|
|
73
|
+
captchaToken: captchaToken.value ?? void 0
|
|
74
|
+
};
|
|
75
|
+
try {
|
|
76
|
+
const customSubmit = options.onSubmit();
|
|
77
|
+
let result;
|
|
78
|
+
if (customSubmit) {
|
|
79
|
+
result = await customSubmit(submission);
|
|
80
|
+
} else {
|
|
81
|
+
const response = await $fetch(
|
|
82
|
+
options.submitEndpoint(),
|
|
83
|
+
{
|
|
84
|
+
method: "POST",
|
|
85
|
+
body: submission
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
result = response;
|
|
89
|
+
}
|
|
90
|
+
if (result.success) {
|
|
91
|
+
isSubmitted.value = true;
|
|
92
|
+
options.onSuccess(submission);
|
|
93
|
+
} else {
|
|
94
|
+
submitError.value = result.error ?? "Submission failed";
|
|
95
|
+
options.onError(submitError.value);
|
|
96
|
+
}
|
|
97
|
+
} catch (e) {
|
|
98
|
+
submitError.value = e?.data?.message || e?.message || "An error occurred";
|
|
99
|
+
options.onError(submitError.value);
|
|
100
|
+
} finally {
|
|
101
|
+
isSubmitting.value = false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
// State
|
|
106
|
+
isOpen,
|
|
107
|
+
isSubmitting,
|
|
108
|
+
isSubmitted,
|
|
109
|
+
submitError,
|
|
110
|
+
// Form
|
|
111
|
+
selectedType,
|
|
112
|
+
email,
|
|
113
|
+
message,
|
|
114
|
+
screenshot,
|
|
115
|
+
honeypot,
|
|
116
|
+
captchaToken,
|
|
117
|
+
// Computed
|
|
118
|
+
feedbackTypes,
|
|
119
|
+
position,
|
|
120
|
+
showScreenshot,
|
|
121
|
+
emailPlaceholder,
|
|
122
|
+
messagePlaceholder,
|
|
123
|
+
captchaEnabled,
|
|
124
|
+
captchaProvider,
|
|
125
|
+
captchaSiteKey,
|
|
126
|
+
isValid,
|
|
127
|
+
// Actions
|
|
128
|
+
open,
|
|
129
|
+
close,
|
|
130
|
+
toggle,
|
|
131
|
+
reset,
|
|
132
|
+
submit,
|
|
133
|
+
captureScreenshot,
|
|
134
|
+
removeScreenshot,
|
|
135
|
+
setCaptchaToken
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DateValue } from "@internationalized/date";
|
|
2
|
+
import { CalendarDate } from "@internationalized/date";
|
|
3
|
+
import type { GanttTask, GanttDependency, GanttZoomLevel, NormalizedGanttTask, GanttTaskTreeNode, GanttLayout } from "../types/gantt-chart.js";
|
|
4
|
+
export interface UseGanttChartOptions {
|
|
5
|
+
tasks: () => GanttTask[];
|
|
6
|
+
dependencies: () => GanttDependency[];
|
|
7
|
+
modelValue: () => Date | string | DateValue | undefined;
|
|
8
|
+
zoomLevel: () => GanttZoomLevel;
|
|
9
|
+
zoomLevels: () => GanttZoomLevel[];
|
|
10
|
+
locale: () => string;
|
|
11
|
+
rowHeight: () => number;
|
|
12
|
+
expanded: () => (string | number)[] | undefined;
|
|
13
|
+
onUpdateModelValue: (date: CalendarDate) => void;
|
|
14
|
+
onUpdateZoomLevel: (level: GanttZoomLevel) => void;
|
|
15
|
+
onUpdateExpanded: (ids: (string | number)[]) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function useGanttChart(options: UseGanttChartOptions): {
|
|
18
|
+
displayDate: import("vue").ComputedRef<CalendarDate>;
|
|
19
|
+
zoomLevel: import("vue").ComputedRef<GanttZoomLevel>;
|
|
20
|
+
zoomLevels: import("vue").ComputedRef<GanttZoomLevel[]>;
|
|
21
|
+
rowHeight: import("vue").ComputedRef<number>;
|
|
22
|
+
normalizedTasks: import("vue").ComputedRef<NormalizedGanttTask[]>;
|
|
23
|
+
normalizedDependencies: import("vue").ComputedRef<GanttDependency[]>;
|
|
24
|
+
taskTree: import("vue").ComputedRef<GanttTaskTreeNode[]>;
|
|
25
|
+
expandedIds: import("vue").Ref<Set<string | number> & Omit<Set<string | number>, keyof Set<any>>, Set<string | number> | (Set<string | number> & Omit<Set<string | number>, keyof Set<any>>)>;
|
|
26
|
+
flatVisible: import("vue").ComputedRef<{
|
|
27
|
+
task: NormalizedGanttTask;
|
|
28
|
+
depth: number;
|
|
29
|
+
isGroup: boolean;
|
|
30
|
+
isExpanded: boolean;
|
|
31
|
+
childCount: number;
|
|
32
|
+
}[]>;
|
|
33
|
+
timelineStart: import("vue").ComputedRef<CalendarDate>;
|
|
34
|
+
timelineEnd: import("vue").ComputedRef<CalendarDate>;
|
|
35
|
+
layout: import("vue").ComputedRef<GanttLayout>;
|
|
36
|
+
headerTitle: import("vue").ComputedRef<string>;
|
|
37
|
+
unitWidth: import("vue").ComputedRef<number>;
|
|
38
|
+
bodyEl: import("vue").Ref<HTMLElement | null, HTMLElement | null>;
|
|
39
|
+
goToPrev: () => void;
|
|
40
|
+
goToNext: () => void;
|
|
41
|
+
goToToday: () => void;
|
|
42
|
+
setZoomLevel: (level: GanttZoomLevel) => void;
|
|
43
|
+
scrollToDate: (date: CalendarDate) => void;
|
|
44
|
+
scrollToTask: (taskId: string | number) => void;
|
|
45
|
+
toggleExpand: (id: string | number) => void;
|
|
46
|
+
expandAll: () => void;
|
|
47
|
+
collapseAll: () => void;
|
|
48
|
+
isExpanded: (id: string | number) => boolean;
|
|
49
|
+
dateToPixel: (date: CalendarDate) => number;
|
|
50
|
+
pixelToDate: (px: number) => CalendarDate;
|
|
51
|
+
getTaskStyle: (task: NormalizedGanttTask) => Record<string, string>;
|
|
52
|
+
};
|